eventmachine-email_server 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/eventmachine-email_server.gemspec +0 -1
- data/lib/eventmachine/email_server.rb +0 -1
- data/lib/eventmachine/email_server/pop3_server.rb +2 -4
- data/lib/eventmachine/email_server/smtp_server.rb +23 -20
- data/lib/eventmachine/email_server/version.rb +1 -1
- data/test/test_email_server.rb +205 -180
- metadata +2 -18
- data/email_server.sqlite3 +0 -0
- data/lib/eventmachine/email_server/sqlite3.rb +0 -153
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21e73cc7eef3bcb21a34c103138939f1f9f216cf
|
4
|
+
data.tar.gz: 03f42527251cde1a286b920f1ee50c84a1318596
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59518897af038bd1fb86ff14cda06f4ec6cd3c4363b2730af532611d8aab3c16123be073973aa08a253dd7c271112efcb613e70aeabed5bd5112f07ce6d86725
|
7
|
+
data.tar.gz: 40f009d49032f6bba06e0a7b4ba215530c788687f648e49e4e1d89f1075a9d60649ef56d62b35262f09852850dab56c46d6ef5a7284c44948ef8873bf9bbf6ec
|
@@ -19,7 +19,6 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_runtime_dependency "eventmachine", ">= 0.12.10"
|
22
|
-
spec.add_runtime_dependency "sqlite3", ">= 1.3.6"
|
23
22
|
spec.add_runtime_dependency "ratelimit-bucketbased", ">= 0.0.1"
|
24
23
|
spec.add_runtime_dependency "eventmachine-dnsbl", ">= 0.0.2"
|
25
24
|
spec.add_runtime_dependency "spf", ">= 0.0.44"
|
@@ -2,7 +2,6 @@ require "eventmachine/email_server/version"
|
|
2
2
|
require 'eventmachine/email_server/base'
|
3
3
|
require 'eventmachine/email_server/memory'
|
4
4
|
require 'eventmachine/email_server/null'
|
5
|
-
require 'eventmachine/email_server/sqlite3'
|
6
5
|
require 'eventmachine/email_server/classifier'
|
7
6
|
require 'eventmachine/email_server/pop3_server'
|
8
7
|
require 'eventmachine/email_server/smtp_server'
|
@@ -188,9 +188,7 @@ module EventMachine
|
|
188
188
|
def stat
|
189
189
|
msgs = bytes = 0
|
190
190
|
@emails.each do |e|
|
191
|
-
|
192
|
-
p e.body.length
|
193
|
-
next if e.marked
|
191
|
+
next if e.marked == 'true'
|
194
192
|
msgs += 1
|
195
193
|
bytes += e.body.length
|
196
194
|
end
|
@@ -213,7 +211,7 @@ module EventMachine
|
|
213
211
|
|
214
212
|
def retr(msgid)
|
215
213
|
msgid = msgid.to_i
|
216
|
-
return false if msgid > @emails.length or @emails[msgid-1].marked
|
214
|
+
return false if msgid > @emails.length or @emails[msgid-1].marked == 'true'
|
217
215
|
@emails[msgid-1].body
|
218
216
|
end
|
219
217
|
|
@@ -86,7 +86,7 @@ module EventMachine
|
|
86
86
|
@spf_ok = true
|
87
87
|
@reject_ok = true
|
88
88
|
@classifier_ok = true
|
89
|
-
@pending_checks =
|
89
|
+
@pending_checks = [:content]
|
90
90
|
end
|
91
91
|
|
92
92
|
def post_init
|
@@ -97,11 +97,7 @@ module EventMachine
|
|
97
97
|
def receive_data(data)
|
98
98
|
puts ">> #{data}" if @debug
|
99
99
|
data.split(/\n/).each do |line|
|
100
|
-
|
101
|
-
if op
|
102
|
-
puts "<< #{op}" if @debug
|
103
|
-
send_data(op+"\r\n")
|
104
|
-
end
|
100
|
+
process_line(line+"\n")
|
105
101
|
end
|
106
102
|
end
|
107
103
|
|
@@ -226,11 +222,16 @@ module EventMachine
|
|
226
222
|
end
|
227
223
|
if @ptr_ok and @rate_ok and @gray_ok and @reject_ok and @dnsbl_ok and @spf_ok and @classifier_ok
|
228
224
|
ans = "250 OK"
|
225
|
+
save
|
229
226
|
else
|
230
227
|
ans = "451 Requested action aborted: local error in processing"
|
231
228
|
end
|
232
|
-
|
233
|
-
|
229
|
+
send(ans)
|
230
|
+
end
|
231
|
+
|
232
|
+
def send(msg)
|
233
|
+
puts "<< #{msg}" if @debug
|
234
|
+
send_data("#{msg}\r\n")
|
234
235
|
end
|
235
236
|
|
236
237
|
def process_line(line)
|
@@ -238,13 +239,12 @@ module EventMachine
|
|
238
239
|
@data_mode = false
|
239
240
|
check_reject
|
240
241
|
check_classifier
|
242
|
+
@pending_checks -= [:content]
|
241
243
|
if @pending_checks.length == 0
|
242
244
|
send_answer
|
243
245
|
end
|
244
|
-
return true, nil
|
245
246
|
elsif @data_mode
|
246
247
|
@email_body += line
|
247
|
-
return true, nil
|
248
248
|
elsif (line =~ /^(HELO|EHLO) (.*)/)
|
249
249
|
helo = $2.chomp
|
250
250
|
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
@@ -252,32 +252,35 @@ module EventMachine
|
|
252
252
|
check_dnsbl(ip)
|
253
253
|
check_gray(ip)
|
254
254
|
check_ratelimit(ip)
|
255
|
-
|
255
|
+
send("250 hello #{ip} (#{helo})")
|
256
256
|
elsif (line =~ /^QUIT/)
|
257
|
-
|
257
|
+
send("221 #{@hostname} ESMTP server closing connection")
|
258
|
+
self.close_connection
|
258
259
|
elsif (line =~ /^MAIL FROM\:/)
|
259
260
|
@mail_from = (/^MAIL FROM\:<(.+)>.*$/).match(line)[1]
|
260
261
|
if @@spf_check
|
261
262
|
port, ip = Socket.unpack_sockaddr_in(get_peername)
|
262
263
|
check_spf(helo, ip, @mail_from)
|
263
264
|
end
|
264
|
-
|
265
|
+
send("250 OK")
|
265
266
|
elsif (line =~ /^RCPT TO\:/)
|
266
267
|
rcpt_to = (/^RCPT TO\:<(.+)>.*$/).match(line)[1]
|
267
268
|
if @userstore.user_by_emailaddress(rcpt_to.strip)
|
268
269
|
@rcpt_to = rcpt_to
|
269
|
-
|
270
|
-
|
271
|
-
|
270
|
+
send("250 OK")
|
271
|
+
else
|
272
|
+
send("550 No such user here")
|
273
|
+
end
|
272
274
|
elsif (line =~ /^DATA/)
|
273
275
|
if @rcpt_to
|
274
276
|
@data_mode = true
|
275
277
|
@email_body = ''
|
276
|
-
|
277
|
-
|
278
|
-
|
278
|
+
send("354 Enter message, ending with \".\" on a line by itself")
|
279
|
+
else
|
280
|
+
send("500 ERROR")
|
281
|
+
end
|
279
282
|
else
|
280
|
-
|
283
|
+
send("500 ERROR")
|
281
284
|
end
|
282
285
|
end
|
283
286
|
|
data/test/test_email_server.rb
CHANGED
@@ -12,138 +12,159 @@ require 'net/pop'
|
|
12
12
|
require 'net/smtp'
|
13
13
|
require 'ratelimit/bucketbased'
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
15
|
+
$dns_port = 53
|
16
|
+
|
17
|
+
class EventMachine::DNS::Socket < EventMachine::Connection
|
18
|
+
def send_packet(pkt)
|
19
|
+
send_datagram(pkt, nameserver, $dns_port)
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
23
|
+
class EmailTemplate < Struct.new(:from, :to, :msg); end
|
24
|
+
|
25
25
|
class TestEmailServer < Minitest::Test
|
26
26
|
def setup
|
27
27
|
@test_vector = Proc.new { |test_name|
|
28
|
+
puts "***** #{test_name} *****"
|
28
29
|
(test_name.to_s =~ /test/)
|
29
30
|
}
|
31
|
+
@spam_email = EmailTemplate.new("friend@example.org", "chris@example.org", "From: friend@example.org
|
32
|
+
To: chris@example.org
|
33
|
+
Subject: What to do when you're not doing.
|
34
|
+
|
35
|
+
Could I interest you in some cialis?
|
36
|
+
")
|
37
|
+
@ham_email = EmailTemplate.new("friend@example.org", "chris@example.org", "From: friend@example.org
|
38
|
+
To: chris@example.org
|
39
|
+
Subject: Good show
|
40
|
+
|
41
|
+
Have you seen the latest Peppa Pig?
|
42
|
+
")
|
43
|
+
@default_email = EmailTemplate.new("friend@example.org", "chris@example.org", "From: friend@example.org
|
44
|
+
To: chris@example.org
|
45
|
+
Subject: Can't remember last night
|
46
|
+
|
47
|
+
Looks like we had fun!
|
48
|
+
")
|
49
|
+
@pool = EM::Pool.new
|
50
|
+
SMTPServer.reset
|
30
51
|
remove_scraps
|
31
52
|
end
|
32
|
-
|
53
|
+
|
33
54
|
def remove_scraps
|
34
55
|
["test.sqlite3", "email_server.sqlite3"].each do |f|
|
35
56
|
if File.exist?("test/#{f}")
|
36
57
|
File.unlink("test/#{f}")
|
37
58
|
end
|
38
59
|
end
|
39
|
-
end
|
40
|
-
|
60
|
+
end
|
61
|
+
|
41
62
|
def teardown
|
42
63
|
remove_scraps
|
43
64
|
end
|
44
|
-
|
65
|
+
|
45
66
|
def setup_user(userstore)
|
46
67
|
userstore << User.new(1, "chris", "chris", "chris@example.org")
|
47
68
|
end
|
48
|
-
|
69
|
+
|
49
70
|
def start_servers(userstore, emailstore)
|
50
71
|
pop3 = EventMachine::start_server "0.0.0.0", 2110, POP3Server, "example.org", userstore, emailstore
|
51
72
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
52
|
-
end
|
53
|
-
|
54
|
-
def send_email(expected_status="250")
|
55
|
-
from = "friend@example.org"
|
56
|
-
to = "chris@example.org"
|
57
|
-
msg = "From: friend@example.org
|
58
|
-
To: chris@example.org
|
59
|
-
Subject: Can't remember last night
|
73
|
+
end
|
60
74
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
smtp = Net::SMTP.
|
65
|
-
ret = smtp.send_message msg, from, to
|
66
|
-
assert_equal(expected_status, ret.status)
|
75
|
+
|
76
|
+
def send_email(email=@default_email, &callback)
|
77
|
+
smtp_dispatcher = EventMachine::ThreadedResource.new do
|
78
|
+
smtp = Net::SMTP.new('localhost', 2025)
|
67
79
|
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def send_spam(expected_status="451")
|
71
|
-
from = "friend@example.org"
|
72
|
-
to = "chris@example.org"
|
73
|
-
msg = "From: friend@example.org
|
74
|
-
To: chris@example.org
|
75
|
-
Subject: What to do when you're not doing.
|
76
80
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
81
|
+
@pool.add smtp_dispatcher
|
82
|
+
|
83
|
+
@pool.perform do |dispatcher|
|
84
|
+
completion = dispatcher.dispatch do |smtp|
|
85
|
+
ret = nil
|
86
|
+
smtp.start do |s|
|
87
|
+
begin
|
88
|
+
ret = s.send_message email.msg, email.from, email.to
|
89
|
+
rescue => e
|
90
|
+
ret = "451"
|
91
|
+
end
|
92
|
+
begin
|
93
|
+
smtp.quit
|
94
|
+
rescue => e
|
95
|
+
end
|
96
|
+
end
|
97
|
+
if ret.respond_to? :status
|
98
|
+
ret = ret.status
|
99
|
+
end
|
100
|
+
ret
|
101
|
+
end
|
102
|
+
|
103
|
+
completion.callback do |result|
|
104
|
+
callback.call(result)
|
105
|
+
end
|
106
|
+
|
107
|
+
completion
|
83
108
|
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def send_ham(expected_status="250")
|
87
|
-
from = "friend@example.org"
|
88
|
-
to = "chris@example.org"
|
89
|
-
msg = "From: friend@example.org
|
90
|
-
To: chris@example.org
|
91
|
-
Subject: Good show
|
109
|
+
end
|
92
110
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
smtp = Net::SMTP.start('localhost', 2025)
|
97
|
-
ret = smtp.send_message msg, from, to
|
98
|
-
assert_equal(expected_status, ret.status)
|
111
|
+
def pop_email(&callback)
|
112
|
+
|
113
|
+
pop3_dispatcher = EventMachine::ThreadedResource.new do
|
99
114
|
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def pop_some_email
|
103
|
-
Thread.new do
|
104
|
-
pop = Net::POP3.APOP(true).new('localhost',2110)
|
105
|
-
pop.start("chris","chris")
|
106
|
-
refute(pop.mails.empty?)
|
107
|
-
pop.each_mail do |m|
|
108
|
-
assert_equal("From: friend@example.org
|
109
|
-
To: chris@example.org
|
110
|
-
Subject: Can't remember last night
|
111
115
|
|
112
|
-
|
113
|
-
|
114
|
-
|
116
|
+
@pool.add pop3_dispatcher
|
117
|
+
|
118
|
+
@pool.perform do |dispatcher|
|
119
|
+
completion = dispatcher.dispatch do |pop|
|
120
|
+
pop = Net::POP3.APOP(true).new('localhost',2110)
|
121
|
+
pop.start("chris","chris")
|
122
|
+
answers = Array.new
|
123
|
+
answers << pop.mails.empty?
|
124
|
+
if not pop.mails.empty?
|
125
|
+
pop.each_mail do |m|
|
126
|
+
answers << m.mail
|
127
|
+
m.delete
|
128
|
+
end
|
129
|
+
end
|
130
|
+
pop.finish
|
131
|
+
answers
|
115
132
|
end
|
116
|
-
end
|
117
|
-
end
|
118
133
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
134
|
+
completion.callback do |answers|
|
135
|
+
callback.call(answers)
|
136
|
+
end
|
137
|
+
|
138
|
+
completion
|
124
139
|
end
|
125
|
-
end
|
140
|
+
end
|
126
141
|
|
127
142
|
def run_test(userstore, emailstore)
|
128
143
|
EM.run {
|
129
144
|
start_servers(userstore, emailstore)
|
130
|
-
|
131
|
-
|
132
|
-
end
|
133
|
-
timer2 = EventMachine::Timer.new(0.2) do
|
134
|
-
pop_some_email
|
135
|
-
end
|
136
|
-
|
137
|
-
timer3 = EventMachine::Timer.new(0.3) do
|
138
|
-
pop_no_email
|
139
|
-
end
|
140
|
-
|
141
|
-
timer4 = EventMachine::Timer.new(0.4) do
|
145
|
+
EM::Timer.new(10) do
|
146
|
+
fail "Test timed out"
|
142
147
|
EM.stop
|
143
148
|
end
|
144
|
-
|
149
|
+
EM::Timer.new(0.1) do
|
150
|
+
pop_email do |answers|
|
151
|
+
assert_equal(true, answers[0])
|
152
|
+
send_email do |result|
|
153
|
+
assert_equal("250", result)
|
154
|
+
pop_email do |answers|
|
155
|
+
assert_equal(false, answers[0])
|
156
|
+
assert_equal(@default_email.msg.gsub(/[\r\n]+/,"\n"), answers[1].gsub(/[\r\n]+/,"\n"))
|
157
|
+
pop_email do |answers|
|
158
|
+
assert_equal(true, answers[0])
|
159
|
+
EM.stop
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
}
|
145
166
|
end
|
146
|
-
|
167
|
+
|
147
168
|
def test_memory_store
|
148
169
|
return unless @test_vector.call(__method__)
|
149
170
|
userstore = MemoryUserStore.new
|
@@ -152,18 +173,8 @@ Looks like we had fun!
|
|
152
173
|
run_test(userstore, emailstore)
|
153
174
|
end
|
154
175
|
|
155
|
-
def test_sqlite3_store
|
156
|
-
return unless @test_vector.call(__method__)
|
157
|
-
s = SQLite3::Database.new("test/test.sqlite3")
|
158
|
-
userstore = Sqlite3UserStore.new(s)
|
159
|
-
emailstore = Sqlite3EmailStore.new(s)
|
160
|
-
setup_user(userstore)
|
161
|
-
run_test(userstore, emailstore)
|
162
|
-
end
|
163
|
-
|
164
176
|
def test_graylisting
|
165
177
|
return unless @test_vector.call(__method__)
|
166
|
-
SMTPServer.reset
|
167
178
|
SMTPServer.graylist(Hash.new)
|
168
179
|
userstore = MemoryUserStore.new
|
169
180
|
emailstore = MemoryEmailStore.new
|
@@ -171,20 +182,23 @@ Looks like we had fun!
|
|
171
182
|
|
172
183
|
EM.run {
|
173
184
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
174
|
-
|
175
|
-
|
176
|
-
send_email("451")
|
177
|
-
end
|
178
|
-
timer2 = EventMachine::Timer.new(0.2) do
|
179
|
-
send_email
|
180
|
-
end
|
181
|
-
timer3 = EventMachine::Timer.new(0.3) do
|
185
|
+
EM::Timer.new(10) do
|
186
|
+
fail "Test timed out"
|
182
187
|
EM.stop
|
183
188
|
end
|
184
|
-
|
189
|
+
|
190
|
+
timer = EventMachine::Timer.new(0.1) do
|
191
|
+
send_email do |result|
|
192
|
+
assert_equal("451", result)
|
193
|
+
send_email do |result|
|
194
|
+
assert_equal("250", result)
|
195
|
+
EM.stop
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
185
199
|
}
|
186
200
|
end
|
187
|
-
|
201
|
+
|
188
202
|
def test_ratelimit
|
189
203
|
return unless @test_vector.call(__method__)
|
190
204
|
config = {
|
@@ -192,7 +206,6 @@ Looks like we had fun!
|
|
192
206
|
}
|
193
207
|
storage = RateLimit::Memory.new
|
194
208
|
rl = RateLimit::BucketBased.new(storage, config, 'default')
|
195
|
-
SMTPServer.reset
|
196
209
|
SMTPServer.ratelimiter(rl)
|
197
210
|
userstore = MemoryUserStore.new
|
198
211
|
emailstore = MemoryEmailStore.new
|
@@ -200,21 +213,26 @@ Looks like we had fun!
|
|
200
213
|
|
201
214
|
EM.run {
|
202
215
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
203
|
-
|
216
|
+
|
217
|
+
EM::Timer.new(10) do
|
218
|
+
fail "Test timed out"
|
219
|
+
EM.stop
|
220
|
+
end
|
204
221
|
timer = EventMachine::Timer.new(0.1) do
|
205
|
-
|
206
|
-
|
207
|
-
|
222
|
+
send_email do |result|
|
223
|
+
assert_equal("250", result)
|
224
|
+
send_email do |result|
|
225
|
+
assert_equal("250", result)
|
226
|
+
send_email do |result|
|
227
|
+
assert_equal("451", result)
|
228
|
+
EM.stop
|
229
|
+
end
|
208
230
|
end
|
209
|
-
send_email("451")
|
210
231
|
end
|
211
232
|
end
|
212
|
-
timer2 = EventMachine::Timer.new(0.3) do
|
213
|
-
EM.stop
|
214
|
-
end
|
215
233
|
}
|
216
234
|
end
|
217
|
-
|
235
|
+
|
218
236
|
def test_reject_list
|
219
237
|
return unless @test_vector.call(__method__)
|
220
238
|
userstore = MemoryUserStore.new
|
@@ -223,25 +241,29 @@ Looks like we had fun!
|
|
223
241
|
|
224
242
|
EM.run {
|
225
243
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
226
|
-
|
244
|
+
|
245
|
+
EM::Timer.new(10) do
|
246
|
+
fail "Test timed out"
|
247
|
+
EM.stop
|
248
|
+
end
|
227
249
|
timer = EventMachine::Timer.new(0.1) do
|
228
|
-
|
229
|
-
|
250
|
+
send_email do |result|
|
251
|
+
assert_equal("250", result)
|
230
252
|
SMTPServer.reject_filters << /remember/
|
253
|
+
send_email do |result|
|
254
|
+
assert_equal("451", result)
|
255
|
+
EM.stop
|
256
|
+
end
|
231
257
|
end
|
232
258
|
end
|
233
|
-
timer2 = EventMachine::Timer.new(0.2) do
|
234
|
-
send_email("451")
|
235
|
-
end
|
236
|
-
timer3 = EventMachine::Timer.new(0.3) do
|
237
|
-
EM.stop
|
238
|
-
end
|
239
|
-
|
240
259
|
}
|
241
260
|
end
|
242
|
-
|
261
|
+
|
243
262
|
def test_dnsbl
|
244
263
|
return unless @test_vector.call(__method__)
|
264
|
+
|
265
|
+
$dns_port = 2053
|
266
|
+
|
245
267
|
#Monkeypatching for testing
|
246
268
|
memzone = EventMachine::DNSBL::Zone::MemoryZone.new
|
247
269
|
EM::DNS::Resolver.nameservers = ["127.0.0.1"]
|
@@ -252,9 +274,8 @@ Looks like we had fun!
|
|
252
274
|
"127.0.0.2" => "Blacklisted as an example"
|
253
275
|
}
|
254
276
|
})
|
255
|
-
SMTPServer.reset
|
256
277
|
SMTPServer.dnsbl_check(true)
|
257
|
-
|
278
|
+
|
258
279
|
userstore = MemoryUserStore.new
|
259
280
|
emailstore = MemoryEmailStore.new
|
260
281
|
setup_user(userstore)
|
@@ -262,106 +283,110 @@ Looks like we had fun!
|
|
262
283
|
EM.run {
|
263
284
|
EM::open_datagram_socket "0.0.0.0", 2053, EventMachine::DNSBL::Server, memzone
|
264
285
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
265
|
-
|
266
|
-
|
286
|
+
EM::Timer.new(10) do
|
287
|
+
fail "Test timed out"
|
288
|
+
EM.stop
|
267
289
|
end
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
290
|
+
timer = EventMachine::Timer.new(0.1) do
|
291
|
+
send_email do |result|
|
292
|
+
assert_equal("250", result)
|
293
|
+
memzone.add_dnsblresource(
|
294
|
+
EventMachine::DNSBL::Zone::DNSBLResourceRecord.new(
|
295
|
+
"example.com",
|
296
|
+
/\d+\.0\.0\.127$/,
|
297
|
+
300,
|
298
|
+
Resolv::DNS::Resource::IN::A.new("127.0.0.4"),
|
299
|
+
Time.now.to_i + 3600
|
300
|
+
)
|
277
301
|
)
|
278
|
-
|
279
|
-
|
280
|
-
|
302
|
+
send_email do |result|
|
303
|
+
assert_equal("451", result)
|
304
|
+
EM.stop
|
305
|
+
end
|
281
306
|
end
|
282
307
|
end
|
283
|
-
|
284
|
-
timer3 = EventMachine::Timer.new(3.0) do
|
285
|
-
EM.stop
|
286
|
-
end
|
287
|
-
|
288
308
|
}
|
289
309
|
end
|
290
|
-
|
310
|
+
|
291
311
|
def test_spf
|
292
312
|
return unless @test_vector.call(__method__)
|
293
313
|
userstore = MemoryUserStore.new
|
294
314
|
emailstore = MemoryEmailStore.new
|
295
315
|
setup_user(userstore)
|
296
|
-
|
297
|
-
|
316
|
+
$dns_port = 53
|
317
|
+
|
298
318
|
SMTPServer.spf_check(true)
|
299
319
|
EM.run {
|
300
320
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
301
|
-
|
302
|
-
|
303
|
-
end
|
304
|
-
timer2 = EventMachine::Timer.new(1) do
|
321
|
+
EM::Timer.new(10) do
|
322
|
+
fail "Test timed out"
|
305
323
|
EM.stop
|
306
324
|
end
|
325
|
+
timer = EventMachine::Timer.new(0.1) do
|
326
|
+
send_email do |result|
|
327
|
+
assert_equal("451", result)
|
328
|
+
EM.stop
|
329
|
+
end
|
330
|
+
end
|
307
331
|
}
|
308
|
-
|
309
332
|
end
|
310
|
-
|
333
|
+
|
311
334
|
def test_classifier
|
312
335
|
return unless @test_vector.call(__method__)
|
313
336
|
userstore = MemoryUserStore.new
|
314
337
|
emailstore = MemoryEmailStore.new
|
315
338
|
setup_user(userstore)
|
316
|
-
SMTPServer.reset
|
317
339
|
classifier = EventMachine::EmailServer::Classifier.new("test/test.classifier", [:spam, :ham], [:spam])
|
318
340
|
classifier.train(:spam, "Amazing pillz viagra cialis levitra staxyn")
|
319
341
|
classifier.train(:ham, "Big pigs make great bacon")
|
320
342
|
SMTPServer.classifier(classifier)
|
321
343
|
EM.run {
|
322
344
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
323
|
-
|
324
|
-
|
325
|
-
send_ham("250")
|
326
|
-
end
|
327
|
-
timer2 = EventMachine::Timer.new(1) do
|
345
|
+
EM::Timer.new(10) do
|
346
|
+
fail "Test timed out"
|
328
347
|
EM.stop
|
329
348
|
end
|
349
|
+
timer = EventMachine::Timer.new(0.1) do
|
350
|
+
send_email(@spam_email) do |result|
|
351
|
+
assert_equal("451", result)
|
352
|
+
send_email(@ham_email) do |result|
|
353
|
+
assert_equal("250", result)
|
354
|
+
EM.stop
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
330
358
|
}
|
331
359
|
end
|
332
|
-
|
360
|
+
|
333
361
|
def test_example
|
334
362
|
return unless @test_vector.call(__method__)
|
335
363
|
#require 'eventmachine/email_server'
|
336
364
|
#include EventMachine::EmailServer
|
337
365
|
#require 'ratelimit/bucketbased'
|
338
366
|
#require 'dnsbl/client'
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
userstore = Sqlite3UserStore.new(s)
|
343
|
-
emailstore = Sqlite3EmailStore.new(s)
|
367
|
+
|
368
|
+
userstore = MemoryUserStore.new()
|
369
|
+
emailstore = MemoryEmailStore.new()
|
344
370
|
userstore << User.new(1, "chris", "chris", "chris@example.org")
|
345
|
-
|
371
|
+
|
346
372
|
config = {
|
347
373
|
'default' => RateLimit::Config.new('default', 2, 2, -2, 1, 1, 1),
|
348
374
|
}
|
349
375
|
storage = RateLimit::Memory.new
|
350
376
|
rl = RateLimit::BucketBased.new(storage, config, 'default')
|
351
|
-
|
377
|
+
|
352
378
|
classifier = EventMachine::EmailServer::Classifier.new("test/test.classifier", [:spam, :ham], [:spam])
|
353
379
|
classifier.train(:spam, "Amazing pillz viagra cialis levitra staxyn")
|
354
380
|
classifier.train(:ham, "Big pigs make great bacon")
|
355
|
-
|
356
|
-
SMTPServer.reset
|
381
|
+
|
357
382
|
SMTPServer.reverse_ptr_check(true)
|
358
383
|
SMTPServer.graylist(Hash.new)
|
359
384
|
SMTPServer.ratelimiter(rl)
|
360
|
-
SMTPServer.dnsbl_check(true)
|
385
|
+
SMTPServer.dnsbl_check(true)
|
361
386
|
SMTPServer.spf_check(true)
|
362
387
|
SMTPServer.reject_filters << /viagra/i
|
363
388
|
SMTPServer.classifier(classifier)
|
364
|
-
|
389
|
+
|
365
390
|
EM.run {
|
366
391
|
pop3 = EventMachine::start_server "0.0.0.0", 2110, POP3Server, "example.org", userstore, emailstore
|
367
392
|
smtp = EventMachine::start_server "0.0.0.0", 2025, SMTPServer, "example.org", userstore, emailstore
|
@@ -370,5 +395,5 @@ Looks like we had fun!
|
|
370
395
|
end
|
371
396
|
}
|
372
397
|
end
|
373
|
-
|
398
|
+
|
374
399
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eventmachine-email_server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- chrislee35
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eventmachine
|
@@ -24,20 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.12.10
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: sqlite3
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 1.3.6
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: 1.3.6
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: ratelimit-bucketbased
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,7 +177,6 @@ files:
|
|
191
177
|
- LICENSE.txt
|
192
178
|
- README.md
|
193
179
|
- Rakefile
|
194
|
-
- email_server.sqlite3
|
195
180
|
- eventmachine-email_server.gemspec
|
196
181
|
- lib/eventmachine/email_server.rb
|
197
182
|
- lib/eventmachine/email_server/base.rb
|
@@ -201,7 +186,6 @@ files:
|
|
201
186
|
- lib/eventmachine/email_server/null.rb
|
202
187
|
- lib/eventmachine/email_server/pop3_server.rb
|
203
188
|
- lib/eventmachine/email_server/smtp_server.rb
|
204
|
-
- lib/eventmachine/email_server/sqlite3.rb
|
205
189
|
- lib/eventmachine/email_server/version.rb
|
206
190
|
- test/helper.rb
|
207
191
|
- test/test_email_server.rb
|
data/email_server.sqlite3
DELETED
Binary file
|
@@ -1,153 +0,0 @@
|
|
1
|
-
require_relative 'base'
|
2
|
-
require 'sqlite3'
|
3
|
-
|
4
|
-
module EventMachine
|
5
|
-
module EmailServer
|
6
|
-
class Sqlite3UserStore < AbstractUserStore
|
7
|
-
def initialize(sqlite3, tablename = "users")
|
8
|
-
@class = User
|
9
|
-
@fields = @class.members.map {|x| x.to_s}.join(", ")
|
10
|
-
@tablename = tablename
|
11
|
-
if sqlite3.class == SQLite3::Database
|
12
|
-
@db = sqlite3
|
13
|
-
else
|
14
|
-
@db = SQLite3::Database.new(sqlite3)
|
15
|
-
end
|
16
|
-
if @db.table_info(tablename).length == 0
|
17
|
-
@db.execute("CREATE TABLE #{@tablename} (#{@fields})")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def add_user(user)
|
22
|
-
if user.id
|
23
|
-
u = user_by_id(user.id)
|
24
|
-
end
|
25
|
-
if u
|
26
|
-
@db.execute("UPDATE #{@tablename} SET username=?, password=?, address=? WHERE id=?",
|
27
|
-
user.username,
|
28
|
-
user.password,
|
29
|
-
user.address,
|
30
|
-
user.id)
|
31
|
-
else
|
32
|
-
@db.execute("INSERT INTO #{@tablename} (id,username,password,address) VALUES (?,?,?,?)",
|
33
|
-
user.id,
|
34
|
-
user.username,
|
35
|
-
user.password,
|
36
|
-
user.address)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def delete_user(user)
|
41
|
-
if user.id
|
42
|
-
@db.execute("DELETE FROM #{@tablename} WHERE id = ?", user.id)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def user_by_field(field, value)
|
47
|
-
rs = @db.execute("SELECT #{@fields} FROM #{@tablename} WHERE #{field}='#{value}'")
|
48
|
-
return nil unless rs
|
49
|
-
rs.each do |row|
|
50
|
-
return User.new(*row)
|
51
|
-
end
|
52
|
-
return nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def user_by_username(username)
|
56
|
-
user_by_field("username", username)
|
57
|
-
end
|
58
|
-
|
59
|
-
def user_by_emailaddress(address)
|
60
|
-
user_by_field("address", address)
|
61
|
-
end
|
62
|
-
|
63
|
-
def user_by_id(id)
|
64
|
-
user_by_field("id", id)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class Sqlite3EmailStore < AbstractEmailStore
|
69
|
-
def initialize(sqlite3, tablename = "emails")
|
70
|
-
@class = Email
|
71
|
-
@fields = "'"+@class.members.map {|x| x.to_s}.join("', '")+"'"
|
72
|
-
@tablename = tablename
|
73
|
-
if sqlite3.class == SQLite3::Database
|
74
|
-
@db = sqlite3
|
75
|
-
else
|
76
|
-
@db = SQLite3::Database.new(sqlite3)
|
77
|
-
end
|
78
|
-
if @db.table_info(tablename).length == 0
|
79
|
-
fields = @fields.gsub(/'id'/, 'id integer primary key autoincrement')
|
80
|
-
@db.execute("CREATE TABLE #{@tablename} (#{fields})")
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def emails_by_field(field, value)
|
85
|
-
rs = @db.execute("SELECT #{@fields} FROM #{@tablename} WHERE ?=?", field, value)
|
86
|
-
return nil unless rs
|
87
|
-
emails = Array.new
|
88
|
-
rs.each do |row|
|
89
|
-
emails << Email.new(*row)
|
90
|
-
end
|
91
|
-
emails
|
92
|
-
end
|
93
|
-
|
94
|
-
def emails_by_userid(uid)
|
95
|
-
emails_by_field("uid", uid)
|
96
|
-
end
|
97
|
-
|
98
|
-
def quote( string )
|
99
|
-
string.gsub( /'/, "''" )
|
100
|
-
end
|
101
|
-
private :quote
|
102
|
-
|
103
|
-
def save_email(email)
|
104
|
-
if email.id
|
105
|
-
# I'm being too crafty here.. this is bad style
|
106
|
-
args = (@class.members - [:id]).map{|f| email.send(f)}
|
107
|
-
args << email.send(:id)
|
108
|
-
@db.execute("UPDATE #{@tablename} SET " +
|
109
|
-
(@class.members - [:id]).map { |field| "#{field} = ?"}.join(", ") +
|
110
|
-
" WHERE id = ?", *args)
|
111
|
-
else
|
112
|
-
email.id = "NULL"
|
113
|
-
args = (@class.members).map{|f| email.send(f)}
|
114
|
-
qs = args.map{|x| "'#{quote(x.to_s)}'"}.join(",").gsub(/'NULL'/, "NULL")
|
115
|
-
@db.execute("INSERT INTO #{@tablename} (#{@fields}) VALUES (#{qs})")
|
116
|
-
rs = @db.execute("SELECT last_insert_rowid()")
|
117
|
-
rs.each do |row|
|
118
|
-
email.id = *row
|
119
|
-
end
|
120
|
-
end
|
121
|
-
email.id
|
122
|
-
end
|
123
|
-
|
124
|
-
def delete_email(email)
|
125
|
-
if email.id
|
126
|
-
@db.execute("DROP FROM #{@tablename} WHERE id = ?", email.id)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def delete_by_field(field, value)
|
131
|
-
@db.execute("DROP FROM #{@tablename} WHERE #{field} = ?", value)
|
132
|
-
end
|
133
|
-
|
134
|
-
def delete_id(id)
|
135
|
-
delete_by_field("id", id)
|
136
|
-
end
|
137
|
-
|
138
|
-
def delete_user(uid)
|
139
|
-
delete_by_field("uid", uid)
|
140
|
-
end
|
141
|
-
|
142
|
-
def count
|
143
|
-
sql = "SELECT COUNT(*) FROM #{@tablename}"
|
144
|
-
rs = @db.execute(sql)
|
145
|
-
c = 0
|
146
|
-
rs.each do |row|
|
147
|
-
c = row[0]
|
148
|
-
end
|
149
|
-
c
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|