maildiode 0.0.2
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/README +85 -0
- data/bin/maildiode +142 -0
- data/doc/index.html +83 -0
- data/lib/alias.rb +47 -0
- data/lib/blacklist.rb +50 -0
- data/lib/delay.rb +48 -0
- data/lib/engine.rb +344 -0
- data/lib/greylist.rb +137 -0
- data/lib/maildir.rb +113 -0
- data/lib/server.rb +93 -0
- data/lib/settings.rb +66 -0
- data/lib/util.rb +107 -0
- data/test/test_engine.rb +239 -0
- data/test/test_suite.rb +20 -0
- metadata +96 -0
data/lib/settings.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright 2007-2008 Kevin B. Smith
|
2
|
+
# This file is part of MailDiode.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
|
17
|
+
module MailDiode
|
18
|
+
|
19
|
+
class Settings
|
20
|
+
def initialize(config_file)
|
21
|
+
@settings = {}
|
22
|
+
File.foreach(config_file) do | line |
|
23
|
+
line = line.gsub(/#.*/, '').strip
|
24
|
+
if !line.empty?
|
25
|
+
component, keyword, args = line.split(' ', 3)
|
26
|
+
load_setting(component.downcase, keyword.downcase, args)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_setting(component, keyword, args)
|
32
|
+
component_settings = get_settings(component)
|
33
|
+
component_settings[keyword] = args
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_settings(component)
|
37
|
+
component_settings = @settings[component]
|
38
|
+
if(!component_settings)
|
39
|
+
component_settings = {}
|
40
|
+
@settings[component] = component_settings
|
41
|
+
end
|
42
|
+
return component_settings
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_setting(component, keyword)
|
46
|
+
component_settings = get_settings(component)
|
47
|
+
if(!component_settings)
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
return component_settings[keyword]
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_int(component, keyword, default_value)
|
54
|
+
value = get_setting(component, keyword)
|
55
|
+
if(!value)
|
56
|
+
return default_value
|
57
|
+
end
|
58
|
+
return value.to_i
|
59
|
+
end
|
60
|
+
|
61
|
+
def Settings.default_file
|
62
|
+
'/etc/maildiode/maildiode.conf'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/util.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Copyright 2007-2008 Kevin B. Smith
|
2
|
+
# This file is part of MailDiode.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
|
17
|
+
require 'logger'
|
18
|
+
|
19
|
+
module MailDiode
|
20
|
+
NEWLINE = "\r\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
module MailDiode
|
25
|
+
class UTCFormatter < Logger::Formatter
|
26
|
+
def initialize
|
27
|
+
@datetime_format = '%Y-%m-%d %H:%M:%SZ'
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(severity, time, progname, msg)
|
31
|
+
time.utc
|
32
|
+
super(severity, time, progname, msg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.log_to_string
|
37
|
+
@log_buffer = StringIO.new('', 'w')
|
38
|
+
log_to_stream(@log_buffer)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.log_to_console
|
42
|
+
@log_buffer = nil
|
43
|
+
log_to_stream(STDERR)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.log_to_file(file_path)
|
47
|
+
is_safe = !file_path.index('..')
|
48
|
+
if !is_safe
|
49
|
+
raise "Log file path cannot contain .."
|
50
|
+
end
|
51
|
+
file_path.untaint
|
52
|
+
@log_buffer = nil
|
53
|
+
out = File.open(file_path, 'a')
|
54
|
+
log_to_stream(out)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.log_to_stream(destination)
|
58
|
+
level = @log ? @log.level : Logger::WARN
|
59
|
+
@log_stream = destination
|
60
|
+
@log = Logger.new(destination, 'monthly')
|
61
|
+
@log.formatter = UTCFormatter.new
|
62
|
+
@log.level = level
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.get_log
|
66
|
+
return @log_buffer.string
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.clear_log
|
70
|
+
@log_buffer.truncate(0)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.flush_log
|
74
|
+
@log_stream.flush
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.log_error(message)
|
78
|
+
@log.error(message)
|
79
|
+
flush_log
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.log_warning(message)
|
83
|
+
@log.warn(message)
|
84
|
+
flush_log
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.log_info(message)
|
88
|
+
@log.info(message)
|
89
|
+
flush_log
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.log_debug(message)
|
93
|
+
@log.debug(message)
|
94
|
+
flush_log
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.log_success(command, args, result)
|
98
|
+
log_info("#{result}: #{command} #{args}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.set_log_level(level)
|
102
|
+
@log.level = level
|
103
|
+
end
|
104
|
+
|
105
|
+
log_to_console
|
106
|
+
|
107
|
+
end
|
data/test/test_engine.rb
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2007-2008 Kevin B. Smith
|
4
|
+
# This file is part of MailDiode.
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License version 3, as
|
8
|
+
# published by the Free Software Foundation.
|
9
|
+
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
require 'test/unit'
|
19
|
+
require 'stringio'
|
20
|
+
|
21
|
+
require 'util'
|
22
|
+
require 'engine'
|
23
|
+
|
24
|
+
FAKE_IP = '10.10.10.10'
|
25
|
+
|
26
|
+
class DummyFilter < MailDiode::Filter
|
27
|
+
attr_accessor :reject
|
28
|
+
attr_reader :data
|
29
|
+
|
30
|
+
def process(filterable_data)
|
31
|
+
if(reject)
|
32
|
+
raise MailDiode::SMTPError.new(reject)
|
33
|
+
end
|
34
|
+
@data = filterable_data
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class DummyMailHandler < MailDiode::MailHandler
|
39
|
+
attr_reader :message_text
|
40
|
+
attr_accessor :reject
|
41
|
+
|
42
|
+
SAMPLE_ID = '12345'
|
43
|
+
|
44
|
+
def initialize
|
45
|
+
@to = []
|
46
|
+
valid = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid_recipient?(to_address)
|
50
|
+
return !reject
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_message(recipient, text_lines)
|
54
|
+
@message_text = text_lines
|
55
|
+
return SAMPLE_ID
|
56
|
+
end
|
57
|
+
|
58
|
+
def message_without_first_line
|
59
|
+
return message_text.split("\r\n")[1..-1].join("\r\n")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class TestEngine < Test::Unit::TestCase
|
64
|
+
def setup
|
65
|
+
MailDiode::set_log_level(Logger::WARN)
|
66
|
+
MailDiode::log_to_console
|
67
|
+
|
68
|
+
@engine = MailDiode::Engine.new('woohoo')
|
69
|
+
@handler = DummyMailHandler.new
|
70
|
+
@engine.set_mail_handler(@handler)
|
71
|
+
|
72
|
+
@greeting = @engine.start(FAKE_IP)
|
73
|
+
end
|
74
|
+
|
75
|
+
def teardown
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_greeting
|
79
|
+
assert_equal(0, @greeting.index('220 '), "Missing 220?")
|
80
|
+
assert_match('woohoo', @greeting, "missing hostname?")
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_unknown_commands
|
84
|
+
assert_equal(0, @engine.process_line('').index(MailDiode::SMTPError::BAD_COMMAND), "Blank cmd accepted?")
|
85
|
+
assert_equal(0, @engine.process_line('xxx').index(MailDiode::SMTPError::BAD_COMMAND), "Bogus cmd accepted?")
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_noop
|
89
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('noop'), "noop failed?")
|
90
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_NOOP, @engine.process_line('noop x'), "noop allowed a parameter?")
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_quit
|
94
|
+
assert(!@engine.terminate?, 'terminating before QUIT?')
|
95
|
+
assert_equal(MailDiode::RESULT_BYE, @engine.process_line('quit'), "didn't say bye?")
|
96
|
+
assert(@engine.terminate?, 'not terminating after QUIT?')
|
97
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_QUIT, @engine.process_line('quit x'), "quit allowed a parameter?")
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_helo
|
101
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_HELO, @engine.process_line('helo'), "helo without arg worked?")
|
102
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_HELO, @engine.process_line('ehlo'), "ehlo without arg worked?")
|
103
|
+
|
104
|
+
helo_result = @engine.process_line('helo localhost')
|
105
|
+
assert_equal(0, helo_result.index('250'), "helo failed?")
|
106
|
+
assert_equal(helo_result, @engine.process_line('helo localhost'), "second helo failed?")
|
107
|
+
assert_equal(helo_result, @engine.process_line('ehlo localhost'), "ehlo failed?")
|
108
|
+
assert_equal(helo_result, @engine.process_line('ehlo [1.2.3.4]'), "ehlo failed?")
|
109
|
+
assert_equal(helo_result, @engine.process_line('ehlo winnie.the.poo'), "ehlo failed?")
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_rset
|
113
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('rset'), "rset failed?")
|
114
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_RSET, @engine.process_line('rset x'), "rset allowed a parameter?")
|
115
|
+
|
116
|
+
@engine.process_line('mail From:<sender@example.com>')
|
117
|
+
@engine.process_line('rset')
|
118
|
+
assert_equal(MailDiode::SMTPError::NEED_MAIL_BEFORE_RCPT, @engine.process_line('rcpt To:<recipient@example.com>'), "rset didn't clear from?")
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_vrfy
|
122
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_VRFY, @engine.process_line('vrfy'), "vrfy without arg worked?")
|
123
|
+
|
124
|
+
assert_equal(MailDiode::RESULT_UNSURE, @engine.process_line('vrfy anything'), "vrfy failed?")
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_mail
|
128
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('mail From:<correct@example.com>'), "mail rejected good from?")
|
129
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('mail From: <correct@example.com>'), "mail rejected because of space after from?")
|
130
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_MAIL, @engine.process_line('mail'), "mail without arg worked?")
|
131
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_MAIL, @engine.process_line('mail foo'), "mail without From: worked?")
|
132
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_MAIL, @engine.process_line('mail from:'), "mail without from arg worked?")
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_rcpt
|
136
|
+
assert_equal(MailDiode::SMTPError::NEED_MAIL_BEFORE_RCPT, @engine.process_line('rcpt To:<correct@example.com>'), "rcpt before mail worked?")
|
137
|
+
|
138
|
+
@engine.process_line('mail From:<correct@example.com>')
|
139
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('rcpt To:<correct@example.com>'), "mail rejected good from?")
|
140
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('rcpt To: <correct@example.com>'), "mail rejected because of space after to?")
|
141
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_RCPT, @engine.process_line('rcpt'), "mail without arg worked?")
|
142
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_RCPT, @engine.process_line('rcpt foo'), "mail without To: worked?")
|
143
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_RCPT, @engine.process_line('rcpt to:'), "mail without to arg worked?")
|
144
|
+
|
145
|
+
@engine.process_line('mail From:<correct@example.com>')
|
146
|
+
99.times do
|
147
|
+
@engine.process_line('rcpt To:<correct@example.com>')
|
148
|
+
end
|
149
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('rcpt To:<correct@example.com>'), "100th recipient rejected?")
|
150
|
+
assert_equal(MailDiode::SMTPError::TOO_MANY_RECIPIENTS, @engine.process_line('rcpt To:<correct@example.com>'), "101st recipient not rejected?")
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_data
|
154
|
+
assert_equal(MailDiode::SMTPError::NEED_RCPT_BEFORE_DATA, @engine.process_line('data'), "data before rcpt worked?")
|
155
|
+
|
156
|
+
@engine.process_line('mail From:<sender@example.com>')
|
157
|
+
@engine.process_line('rcpt To:<recipient@example.com>')
|
158
|
+
assert_equal(MailDiode::SMTPError::SYNTAX_DATA, @engine.process_line('data xxx'), "data with arg accepted?")
|
159
|
+
assert_equal(MailDiode::RESULT_DATA_OK, @engine.process_line('data'), "data rejected?")
|
160
|
+
assert_equal(nil, @engine.process_line('This is message data'), "responded to message data?")
|
161
|
+
@engine.process_line('Second line')
|
162
|
+
assert_equal(0, @engine.process_line('.').index(MailDiode::RESULT_OK), "didn't pretend to save?")
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
|
169
|
+
def test_filter
|
170
|
+
filter = DummyFilter.new
|
171
|
+
@engine.add_filter(filter)
|
172
|
+
@engine.process_line('mail From:<sender@example.com>')
|
173
|
+
@engine.process_line('rcpt To:<recipient@example.com>')
|
174
|
+
assert_equal('sender@example.com', filter.data.sender, "didn't call handler validate_sender?")
|
175
|
+
assert_equal('recipient@example.com', filter.data.recipient, "didn't call handler validate_recipient?")
|
176
|
+
|
177
|
+
filter.reject = "450 Try Later"
|
178
|
+
assert_equal(filter.reject, @engine.process_line('rcpt To:<recipient@example.com>'), "filter rejection not propagated?")
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_mail_handler
|
182
|
+
@engine.process_line('mail From:<sender@example.com>')
|
183
|
+
@engine.process_line('rcpt To:<recipient@example.com>')
|
184
|
+
|
185
|
+
sample_text = ['First line', 'Second', '', ' . not a dot line', 'After blank']
|
186
|
+
@engine.process_line('DATA')
|
187
|
+
sample_text.each do | line |
|
188
|
+
@engine.process_line(line)
|
189
|
+
end
|
190
|
+
assert_equal(0, @engine.process_line('.').index(MailDiode::RESULT_OK), "handler didn't accept?")
|
191
|
+
assert_equal(sample_text.join("\r\n"), @handler.message_without_first_line, "bad message data?")
|
192
|
+
|
193
|
+
assert_equal(MailDiode::SMTPError::NEED_RCPT_BEFORE_DATA, @engine.process_line('data'), "didn't reset after .?")
|
194
|
+
|
195
|
+
MailDiode::set_log_level(Logger::INFO)
|
196
|
+
MailDiode::log_to_string
|
197
|
+
|
198
|
+
@handler.reject = true
|
199
|
+
@engine.process_line('mail From:<sender@example.com>')
|
200
|
+
assert_equal(MailDiode::SMTPError::UNKNOWN_RECIPIENT, @engine.process_line('rcpt To:<recipient@example.com>'), "reject to not propagated?")
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_dot_in_data
|
204
|
+
@engine.process_line('mail From:<sender@example.com>')
|
205
|
+
@engine.process_line('rcpt To:<recipient@example.com>')
|
206
|
+
@engine.process_line('data')
|
207
|
+
line_with_dot = '..Line with a dot'
|
208
|
+
@engine.process_line(line_with_dot)
|
209
|
+
@engine.process_line('.')
|
210
|
+
assert_equal(line_with_dot[1..-1], @handler.message_without_first_line, "didn't unescape the dot?")
|
211
|
+
end
|
212
|
+
|
213
|
+
def test_args
|
214
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('noop '), "choked on trailing space?")
|
215
|
+
assert_equal(0, @engine.process_line("helo\tx").index('250'), "choked on tab?")
|
216
|
+
end
|
217
|
+
|
218
|
+
def test_case_insensitive_commands
|
219
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('noop'), "lower case failed?")
|
220
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('NOOP'), "upper case failed?")
|
221
|
+
assert_equal(MailDiode::RESULT_OK, @engine.process_line('nOOp'), "mixed case failed?")
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_normal_logging
|
225
|
+
MailDiode::log_to_string
|
226
|
+
@engine.process_line('noop')
|
227
|
+
assert_equal('', MailDiode::get_log)
|
228
|
+
MailDiode::clear_log
|
229
|
+
@engine.process_line('xxx')
|
230
|
+
assert_equal('', MailDiode::get_log)
|
231
|
+
end
|
232
|
+
|
233
|
+
def test_verbose_logging
|
234
|
+
MailDiode::set_log_level(Logger::INFO)
|
235
|
+
MailDiode::log_to_string
|
236
|
+
@engine.process_line('noop')
|
237
|
+
assert(MailDiode::get_log.index('INFO'))
|
238
|
+
end
|
239
|
+
end
|
data/test/test_suite.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2007-2008 Kevin B. Smith
|
4
|
+
# This file is part of MailDiode.
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License version 3, as
|
8
|
+
# published by the Free Software Foundation.
|
9
|
+
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
require 'test/unit'
|
19
|
+
|
20
|
+
require 'test_engine.rb'
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maildiode
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kevin Smith
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-03 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: daemons
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.0.10
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: gurgitate-mail
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.10.0
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: KirbyBase
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.6.0
|
44
|
+
version:
|
45
|
+
description:
|
46
|
+
email: kevins@qualitycode.com
|
47
|
+
executables:
|
48
|
+
- maildiode
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README
|
53
|
+
- doc/index.html
|
54
|
+
files:
|
55
|
+
- bin/maildiode
|
56
|
+
- lib/alias.rb
|
57
|
+
- lib/blacklist.rb
|
58
|
+
- lib/delay.rb
|
59
|
+
- lib/engine.rb
|
60
|
+
- lib/greylist.rb
|
61
|
+
- lib/maildir.rb
|
62
|
+
- lib/server.rb
|
63
|
+
- lib/util.rb
|
64
|
+
- lib/settings.rb
|
65
|
+
- test/test_engine.rb
|
66
|
+
- test/test_suite.rb
|
67
|
+
- README
|
68
|
+
- doc/index.html
|
69
|
+
has_rdoc: false
|
70
|
+
homepage: http://maildiode.rubyforge.org/
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 1.8.1
|
81
|
+
version:
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: "0"
|
87
|
+
version:
|
88
|
+
requirements: []
|
89
|
+
|
90
|
+
rubyforge_project: maildiode
|
91
|
+
rubygems_version: 1.2.0
|
92
|
+
signing_key:
|
93
|
+
specification_version: 2
|
94
|
+
summary: MailDiode is a simple incoming SMTP server daemon.
|
95
|
+
test_files:
|
96
|
+
- test/test_suite.rb
|