received 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/received +1 -0
- data/lib/received/backend/mongodb.rb +2 -1
- data/lib/received/connection.rb +10 -3
- data/lib/received/lmtp.rb +9 -2
- data/lib/received/version.rb +1 -1
- data/spec/lmtp_spec.rb +42 -20
- metadata +4 -26
data/bin/received
CHANGED
@@ -46,6 +46,7 @@ if options.delete(:daemon)
|
|
46
46
|
# Drop privileges if started as superuser
|
47
47
|
params.merge!({:user => options[:user] || 'nobody', :group => options[:group] || 'nobody'}) if Process.uid == 0
|
48
48
|
Daemons.daemonize(params)
|
49
|
+
File.umask(0007)
|
49
50
|
end
|
50
51
|
|
51
52
|
server = Received::Server.new(options)
|
@@ -18,8 +18,9 @@ module Received
|
|
18
18
|
# Store mail in MongoDB
|
19
19
|
#
|
20
20
|
# @param [Hash] mail
|
21
|
+
# @return [ObjectId] object_id
|
21
22
|
def store(mail)
|
22
|
-
@coll.
|
23
|
+
@coll.insert(mail.merge({:ts => Time.now.to_i}), :safe => {:fsync => true})
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
data/lib/received/connection.rb
CHANGED
@@ -34,12 +34,19 @@ module Received
|
|
34
34
|
#
|
35
35
|
# @param [Hash] mail
|
36
36
|
# @see Received::Backend::Base#store
|
37
|
+
# @return [Boolean] saving succeeded
|
37
38
|
def mail_received(mail)
|
38
39
|
begin
|
39
|
-
@backend.store(mail)
|
40
|
-
|
40
|
+
if insert_id = @backend.store(mail)
|
41
|
+
logger.info "stored mail from: #{mail[:from]} (#{insert_id})"
|
42
|
+
return true
|
43
|
+
else
|
44
|
+
logger.error "saving of mail from #{mail[:from]} failed"
|
45
|
+
end
|
46
|
+
false
|
41
47
|
rescue Exception => e
|
42
|
-
logger.error "saving failed with: #{e.message}"
|
48
|
+
logger.error "saving of mail from #{mail[:from]} failed with: #{e.message}"
|
49
|
+
false
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
data/lib/received/lmtp.rb
CHANGED
@@ -71,9 +71,12 @@ module Received
|
|
71
71
|
end
|
72
72
|
when :data
|
73
73
|
if ev == ".\r\n"
|
74
|
-
@rcpt.size.times {ok}
|
75
74
|
mail = {:from => @from, :rcpt => @rcpt, :body => @body.join}
|
76
|
-
@conn.mail_received(mail)
|
75
|
+
if @conn.mail_received(mail)
|
76
|
+
@rcpt.size.times {ok}
|
77
|
+
else
|
78
|
+
@rcpt.size.times {error_in_processing}
|
79
|
+
end
|
77
80
|
:data_received
|
78
81
|
else
|
79
82
|
@body << ev
|
@@ -123,6 +126,10 @@ module Received
|
|
123
126
|
emit "500 command unrecognized"
|
124
127
|
end
|
125
128
|
|
129
|
+
def error_in_processing
|
130
|
+
emit "451 Requested action aborted: local error in processing"
|
131
|
+
end
|
132
|
+
|
126
133
|
def emit(str)
|
127
134
|
@conn.send_data "#{str}\r\n"
|
128
135
|
# return nil, so there won't be implicit state transition
|
data/lib/received/version.rb
CHANGED
data/spec/lmtp_spec.rb
CHANGED
@@ -5,33 +5,55 @@ describe Received::LMTP do
|
|
5
5
|
before :each do
|
6
6
|
@mock = mock 'conn'
|
7
7
|
@mock.should_receive(:send_data).with("220 localhost LMTP server ready\r\n")
|
8
|
-
|
8
|
+
logger = Logger.new($stderr)
|
9
|
+
@mock.stub!(:logger).and_return(logger)
|
9
10
|
@mock.logger.debug "*** Starting test ***"
|
10
11
|
@proto = Received::LMTP.new(@mock)
|
11
12
|
@proto.start!
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
:from => 'spec1@example.com',
|
24
|
-
:rcpt => ['spec2@example.com', 'spec3@example.com'],
|
25
|
-
:body => body
|
26
|
-
})
|
27
|
-
@mock.should_receive(:close_connection_after_writing)
|
28
|
-
|
29
|
-
["LHLO", "MAIL FROM:<spec1@example.com>", "RCPT TO:<spec2@example.com>",
|
30
|
-
"RCPT TO:<spec3@example.com>", "DATA", "#{body}.", "QUIT"].each do |line|
|
31
|
-
@mock.logger.debug "client: #{line}"
|
32
|
-
@proto.on_data(line + "\r\n")
|
15
|
+
describe "Full flow" do
|
16
|
+
let(:body) { "Subject: spec\r\nspec\r\n" }
|
17
|
+
|
18
|
+
def begin_flow!
|
19
|
+
["LHLO", "MAIL FROM:<spec1@example.com>", "RCPT TO:<spec2@example.com>",
|
20
|
+
"RCPT TO:<spec3@example.com>", "DATA", "#{body}.", "QUIT"].each do |line|
|
21
|
+
@mock.logger.debug "client: #{line}"
|
22
|
+
@proto.on_data(line + "\r\n")
|
23
|
+
end
|
33
24
|
end
|
34
25
|
|
26
|
+
def common_expectations!
|
27
|
+
@mock.should_receive(:send_data).with("250-localhost\r\n")
|
28
|
+
@mock.should_receive(:send_data).with("250-8BITMIME\r\n250 PIPELINING\r\n")
|
29
|
+
@mock.should_receive(:send_data).with("250 OK\r\n").exactly(3).times
|
30
|
+
@mock.should_receive(:send_data).with("354 End data with <CR><LF>.<CR><LF>\r\n")
|
31
|
+
end
|
32
|
+
|
33
|
+
it "receives mail" do
|
34
|
+
common_expectations!
|
35
|
+
@mock.should_receive(:send_data).with("250 OK\r\n").exactly(2).times
|
36
|
+
@mock.should_receive(:send_data).with("221 Bye\r\n")
|
37
|
+
@mock.should_receive(:mail_received).with({
|
38
|
+
:from => 'spec1@example.com',
|
39
|
+
:rcpt => ['spec2@example.com', 'spec3@example.com'],
|
40
|
+
:body => body
|
41
|
+
}).and_return(true)
|
42
|
+
@mock.should_receive(:close_connection_after_writing)
|
43
|
+
|
44
|
+
begin_flow!
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
it "returns error when it cannot save email" do
|
49
|
+
common_expectations!
|
50
|
+
@mock.should_receive(:mail_received).once.and_return(false)
|
51
|
+
@mock.should_receive(:send_data).with(/451/).exactly(2).times
|
52
|
+
@mock.should_receive(:send_data).with("221 Bye\r\n")
|
53
|
+
@mock.should_receive(:close_connection_after_writing)
|
54
|
+
|
55
|
+
begin_flow!
|
56
|
+
end
|
35
57
|
end
|
36
58
|
|
37
59
|
it "parses multiline" do
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: received
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 2
|
8
|
-
- 0
|
9
|
-
version: 0.2.0
|
4
|
+
prerelease:
|
5
|
+
version: 0.3.0
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Roman Shterenzon
|
@@ -14,8 +10,7 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date: 2011-
|
18
|
-
default_executable:
|
13
|
+
date: 2011-09-13 00:00:00 Z
|
19
14
|
dependencies:
|
20
15
|
- !ruby/object:Gem::Dependency
|
21
16
|
name: daemons
|
@@ -25,8 +20,6 @@ dependencies:
|
|
25
20
|
requirements:
|
26
21
|
- - ">="
|
27
22
|
- !ruby/object:Gem::Version
|
28
|
-
segments:
|
29
|
-
- 0
|
30
23
|
version: "0"
|
31
24
|
type: :runtime
|
32
25
|
version_requirements: *id001
|
@@ -38,8 +31,6 @@ dependencies:
|
|
38
31
|
requirements:
|
39
32
|
- - ">="
|
40
33
|
- !ruby/object:Gem::Version
|
41
|
-
segments:
|
42
|
-
- 0
|
43
34
|
version: "0"
|
44
35
|
type: :runtime
|
45
36
|
version_requirements: *id002
|
@@ -51,10 +42,6 @@ dependencies:
|
|
51
42
|
requirements:
|
52
43
|
- - ~>
|
53
44
|
- !ruby/object:Gem::Version
|
54
|
-
segments:
|
55
|
-
- 1
|
56
|
-
- 3
|
57
|
-
- 0
|
58
45
|
version: 1.3.0
|
59
46
|
type: :runtime
|
60
47
|
version_requirements: *id003
|
@@ -66,10 +53,6 @@ dependencies:
|
|
66
53
|
requirements:
|
67
54
|
- - ~>
|
68
55
|
- !ruby/object:Gem::Version
|
69
|
-
segments:
|
70
|
-
- 1
|
71
|
-
- 3
|
72
|
-
- 0
|
73
56
|
version: 1.3.0
|
74
57
|
type: :runtime
|
75
58
|
version_requirements: *id004
|
@@ -100,7 +83,6 @@ files:
|
|
100
83
|
- received.gemspec
|
101
84
|
- spec/lmtp_spec.rb
|
102
85
|
- spec/spec_helper.rb
|
103
|
-
has_rdoc: true
|
104
86
|
homepage: ""
|
105
87
|
licenses: []
|
106
88
|
|
@@ -114,21 +96,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
96
|
requirements:
|
115
97
|
- - ">="
|
116
98
|
- !ruby/object:Gem::Version
|
117
|
-
segments:
|
118
|
-
- 0
|
119
99
|
version: "0"
|
120
100
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
101
|
none: false
|
122
102
|
requirements:
|
123
103
|
- - ">="
|
124
104
|
- !ruby/object:Gem::Version
|
125
|
-
segments:
|
126
|
-
- 0
|
127
105
|
version: "0"
|
128
106
|
requirements: []
|
129
107
|
|
130
108
|
rubyforge_project:
|
131
|
-
rubygems_version: 1.
|
109
|
+
rubygems_version: 1.8.10
|
132
110
|
signing_key:
|
133
111
|
specification_version: 3
|
134
112
|
summary: Receive mail from Postfix and store it somewhere
|