rims 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +19 -17
- data/ChangeLog +36 -0
- data/Rakefile +70 -1
- data/lib/rims.rb +3 -5
- data/lib/rims/auth.rb +5 -7
- data/lib/rims/cmd.rb +758 -188
- data/lib/rims/error.rb +8 -10
- data/lib/rims/kvs.rb +4 -0
- data/lib/rims/lock.rb +4 -1
- data/lib/rims/protocol.rb +7 -1
- data/lib/rims/protocol/decoder.rb +24 -7
- data/lib/rims/protocol/parser.rb +1 -1
- data/lib/rims/service.rb +953 -0
- data/lib/rims/version.rb +1 -1
- data/rims.gemspec +2 -0
- data/test/cmd/test_command.rb +999 -0
- data/test/test_auth.rb +3 -1
- data/test/test_cmd.rb +36 -0
- data/test/test_error.rb +22 -78
- data/test/test_protocol_auth.rb +3 -1
- data/test/test_protocol_decoder.rb +6 -3
- data/test/test_service.rb +1120 -0
- metadata +38 -11
- data/lib/rims/daemon.rb +0 -338
- data/lib/rims/server.rb +0 -567
- data/test/test_config.rb +0 -533
- data/test/test_daemon_status_file.rb +0 -169
- data/test/test_daemon_waitpid.rb +0 -72
data/lib/rims/error.rb
CHANGED
@@ -2,17 +2,15 @@
|
|
2
2
|
|
3
3
|
module RIMS
|
4
4
|
class Error < StandardError
|
5
|
-
def self.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
nil
|
12
|
-
end
|
13
|
-
else
|
14
|
-
yield
|
5
|
+
def self.trace_error_chain(exception)
|
6
|
+
return enum_for(:trace_error_chain, exception) unless block_given?
|
7
|
+
|
8
|
+
while (exception)
|
9
|
+
yield(exception)
|
10
|
+
exception = exception.cause
|
15
11
|
end
|
12
|
+
|
13
|
+
nil
|
16
14
|
end
|
17
15
|
end
|
18
16
|
end
|
data/lib/rims/kvs.rb
CHANGED
data/lib/rims/lock.rb
CHANGED
@@ -136,7 +136,10 @@ module RIMS
|
|
136
136
|
retry
|
137
137
|
rescue
|
138
138
|
logger.error('unexpected error at a detached thread and give up to retry write-lock timeout error.')
|
139
|
-
|
139
|
+
Error.trace_error_chain($!) do |exception|
|
140
|
+
logger.error(exception)
|
141
|
+
end
|
142
|
+
|
140
143
|
$!
|
141
144
|
end
|
142
145
|
}
|
data/lib/rims/protocol.rb
CHANGED
@@ -31,13 +31,19 @@ module RIMS
|
|
31
31
|
end
|
32
32
|
module_function :compile_wildcard
|
33
33
|
|
34
|
+
IO_DATA_DUMP = false # true for debug
|
35
|
+
|
34
36
|
def io_data_log(str)
|
35
37
|
s = '<'
|
36
38
|
s << str.encoding.to_s
|
37
39
|
if (str.ascii_only?) then
|
38
40
|
s << ':ascii_only'
|
39
41
|
end
|
40
|
-
|
42
|
+
if (IO_DATA_DUMP) then
|
43
|
+
s << '> ' << str.inspect
|
44
|
+
else
|
45
|
+
s << '> ' << str.bytesize.to_s << ' octets'
|
46
|
+
end
|
41
47
|
end
|
42
48
|
module_function :io_data_log
|
43
49
|
|
@@ -37,7 +37,9 @@ module RIMS
|
|
37
37
|
atom_list = request_reader.read_command
|
38
38
|
rescue
|
39
39
|
logger.error('invalid client command.')
|
40
|
-
|
40
|
+
Error.trace_error_chain($!) do |exception|
|
41
|
+
logger.error(exception)
|
42
|
+
end
|
41
43
|
response_write.call([ "* BAD client command syntax error\r\n" ])
|
42
44
|
next
|
43
45
|
end
|
@@ -46,7 +48,16 @@ module RIMS
|
|
46
48
|
|
47
49
|
tag, command, *opt_args = atom_list
|
48
50
|
logger.info("client command: #{tag} #{command}")
|
49
|
-
|
51
|
+
if (logger.debug?) then
|
52
|
+
case (command.upcase)
|
53
|
+
when 'LOGIN'
|
54
|
+
log_opt_args = opt_args.dup
|
55
|
+
log_opt_args[-1] = '********'
|
56
|
+
else
|
57
|
+
log_opt_args = opt_args
|
58
|
+
end
|
59
|
+
logger.debug("client command parameter: #{log_opt_args.inspect}")
|
60
|
+
end
|
50
61
|
|
51
62
|
begin
|
52
63
|
case (command.upcase)
|
@@ -126,7 +137,9 @@ module RIMS
|
|
126
137
|
end
|
127
138
|
rescue
|
128
139
|
logger.error('unexpected error.')
|
129
|
-
|
140
|
+
Error.trace_error_chain($!) do |exception|
|
141
|
+
logger.error(exception)
|
142
|
+
end
|
130
143
|
response_write.call([ "#{tag} BAD unexpected error\r\n" ])
|
131
144
|
end
|
132
145
|
|
@@ -139,7 +152,7 @@ module RIMS
|
|
139
152
|
|
140
153
|
nil
|
141
154
|
ensure
|
142
|
-
|
155
|
+
decoder.cleanup
|
143
156
|
end
|
144
157
|
|
145
158
|
def initialize(auth, logger)
|
@@ -158,7 +171,9 @@ module RIMS
|
|
158
171
|
rescue
|
159
172
|
raise if ($!.class.name =~ /AssertionFailedError/)
|
160
173
|
@logger.error('internal server error.')
|
161
|
-
|
174
|
+
Error.trace_error_chain($!) do |exception|
|
175
|
+
@logger.error(exception)
|
176
|
+
end
|
162
177
|
res << "#{tag} BAD internal server error\r\n"
|
163
178
|
end
|
164
179
|
}
|
@@ -183,7 +198,9 @@ module RIMS
|
|
183
198
|
rescue
|
184
199
|
raise if ($!.class.name =~ /AssertionFailedError/)
|
185
200
|
@logger.error('internal server error.')
|
186
|
-
|
201
|
+
Error.trace_error_chain($!) do |exception|
|
202
|
+
@logger.error(exception)
|
203
|
+
end
|
187
204
|
yield([ "#{tag} BAD internal server error\r\n" ])
|
188
205
|
end
|
189
206
|
end
|
@@ -245,7 +262,7 @@ module RIMS
|
|
245
262
|
|
246
263
|
class InitialDecoder < Decoder
|
247
264
|
def initialize(mail_store_pool, auth, logger,
|
248
|
-
mail_delivery_user:
|
265
|
+
mail_delivery_user: Service::DEFAULT_CONFIG.mail_delivery_user,
|
249
266
|
write_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
|
250
267
|
**next_decoder_optional)
|
251
268
|
super(auth, logger)
|
data/lib/rims/protocol/parser.rb
CHANGED
@@ -190,7 +190,7 @@ module RIMS
|
|
190
190
|
|
191
191
|
def read_client_response_data_plain(inline_client_response_data_base64)
|
192
192
|
if (inline_client_response_data_base64) then
|
193
|
-
@logger.debug("authenticate command: inline client response data: #{inline_client_response_data_base64}") if @logger.debug?
|
193
|
+
@logger.debug("authenticate command: inline client response data: #{Protocol.io_data_log(inline_client_response_data_base64)}") if @logger.debug?
|
194
194
|
Protocol.decode_base64(inline_client_response_data_base64)
|
195
195
|
else
|
196
196
|
read_client_response_data
|
data/lib/rims/service.rb
ADDED
@@ -0,0 +1,953 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
require 'json'
|
5
|
+
require 'logger'
|
6
|
+
require 'logger/joint'
|
7
|
+
require 'pathname'
|
8
|
+
require 'riser'
|
9
|
+
require 'socket'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
module RIMS
|
13
|
+
class Service
|
14
|
+
class Configuration
|
15
|
+
class << self
|
16
|
+
def stringify_symbol(collection)
|
17
|
+
case (collection)
|
18
|
+
when Hash
|
19
|
+
Hash[collection.map{|key, value| [ stringify_symbol(key), stringify_symbol(value) ] }]
|
20
|
+
when Array
|
21
|
+
collection.map{|i| stringify_symbol(i) }
|
22
|
+
else
|
23
|
+
case (value = collection)
|
24
|
+
when Symbol
|
25
|
+
value.to_s
|
26
|
+
else
|
27
|
+
value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(dest, other)
|
33
|
+
case (dest)
|
34
|
+
when Hash
|
35
|
+
unless (other.is_a? Hash) then
|
36
|
+
raise ArgumentError, 'hash can only be updated with hash.'
|
37
|
+
end
|
38
|
+
for key, value in other
|
39
|
+
dest[key] = update(dest[key], value)
|
40
|
+
end
|
41
|
+
dest
|
42
|
+
when Array
|
43
|
+
if (other.is_a? Array) then
|
44
|
+
dest.concat(other)
|
45
|
+
else
|
46
|
+
other
|
47
|
+
end
|
48
|
+
else
|
49
|
+
other
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_configuration(collection, base_dir=nil)
|
54
|
+
if ((collection.key? 'configuration') && (collection.key? 'configuration_file')) then
|
55
|
+
raise KeyError, 'configuration conflict: configuration, configuraion_file'
|
56
|
+
end
|
57
|
+
|
58
|
+
if (collection.key? 'configuration') then
|
59
|
+
collection['configuration']
|
60
|
+
elsif (collection.key? 'configuration_file') then
|
61
|
+
configuration_file_path = Pathname(collection['configuration_file'])
|
62
|
+
if (configuration_file_path.relative?) then
|
63
|
+
base_dir or raise ArgumentError, 'need for base_dir.'
|
64
|
+
configuration_file_path = base_dir + configuration_file_path # expect base_dir to be Pathname
|
65
|
+
end
|
66
|
+
YAML.load_file(configuration_file_path.to_s)
|
67
|
+
else
|
68
|
+
{}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@config = {}
|
75
|
+
end
|
76
|
+
|
77
|
+
def load(config, load_path=nil)
|
78
|
+
stringified_config = self.class.stringify_symbol(config)
|
79
|
+
if (stringified_config.key? 'base_dir') then
|
80
|
+
base_dir = Pathname(stringified_config['base_dir'])
|
81
|
+
if (load_path && base_dir.relative?) then
|
82
|
+
stringified_config['base_dir'] = Pathname(load_path) + base_dir
|
83
|
+
else
|
84
|
+
stringified_config['base_dir'] = base_dir
|
85
|
+
end
|
86
|
+
elsif (load_path) then
|
87
|
+
stringified_config['base_dir'] = Pathname(load_path)
|
88
|
+
end
|
89
|
+
self.class.update(@config, stringified_config)
|
90
|
+
|
91
|
+
self
|
92
|
+
end
|
93
|
+
|
94
|
+
# configuration example.
|
95
|
+
# required_features:
|
96
|
+
# - rims/qdbm
|
97
|
+
# - rims/passwd/ldap
|
98
|
+
# logging:
|
99
|
+
# file:
|
100
|
+
# path: rims.log
|
101
|
+
# shift_age: 10
|
102
|
+
# shift_size: 1048576
|
103
|
+
# level: debug
|
104
|
+
# datetime_format: %Y-%m-%d %H:%M:%S
|
105
|
+
# shift_period_suffix: %Y%m%d
|
106
|
+
# stdout:
|
107
|
+
# level: info
|
108
|
+
# datetime_format: %Y-%m-%d %H:%M:%S
|
109
|
+
# protocol:
|
110
|
+
# # default is not output.
|
111
|
+
# # to output, set the log level to info or less.
|
112
|
+
# path: protocol.log
|
113
|
+
# shift_age: 10
|
114
|
+
# shift_size: 1048576
|
115
|
+
# level: info
|
116
|
+
# datetime_format: %Y-%m-%d %H:%M:%S
|
117
|
+
# shift_period_suffix: %Y%m%d
|
118
|
+
# daemon:
|
119
|
+
# daemonize: true
|
120
|
+
# debug: false
|
121
|
+
# status_file: rims.pid
|
122
|
+
# server_polling_interval_seconds: 3
|
123
|
+
# server_privileged_user: nobody
|
124
|
+
# server_privileged_group: nogroup
|
125
|
+
# server:
|
126
|
+
# listen_address:
|
127
|
+
# # see `Riser::SocketAddress.parse' for address format
|
128
|
+
# type: tcp
|
129
|
+
# host: 0.0.0.0
|
130
|
+
# port: 143
|
131
|
+
# backlog: 64
|
132
|
+
# accept_polling_timeout_seconds: 0.1
|
133
|
+
# thread_num: 20
|
134
|
+
# thread_queue_size: 20
|
135
|
+
# thread_queue_polling_timeout_seconds: 0.1
|
136
|
+
# send_buffer_limit_size: 16384
|
137
|
+
# openssl:
|
138
|
+
# use_ssl: true
|
139
|
+
# ssl_context: |
|
140
|
+
# # this entry is evaluated in an anonymous ruby module
|
141
|
+
# # including OpenSSL to initialize the SSLContext used
|
142
|
+
# # for TLS connection.
|
143
|
+
# # SSLContext object is stored at `_'.
|
144
|
+
# # Pathname object is stored at `base_dir'.
|
145
|
+
# _.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_default.cert").read)
|
146
|
+
# _.key = PKey.read((base_dir / "tls_secret" / "server.priv_key").read)
|
147
|
+
# sni_tbl = {
|
148
|
+
# "imap.example.com" => SSLContext.new.tap{|c| c.key = _.key; c.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_imap.cert").read) },
|
149
|
+
# "imap2.example.com" => SSLContext.new.tap{|c| c.key = _.key; c.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_imap2.cert").read) },
|
150
|
+
# "imap3.example.com" => SSLContext.new.tap{|c| c.key = _.key; c.cert = X509::Certificate.new((base_dir / "tls_cert" / "server_imap3.cert").read) }
|
151
|
+
# }
|
152
|
+
# _.servername_cb = lambda{|ssl_socket, hostname| sni_tbl[hostname.downcase] }
|
153
|
+
# lock:
|
154
|
+
# read_lock_timeout_seconds: 30
|
155
|
+
# write_lock_timeout_seconds: 30
|
156
|
+
# cleanup_write_lock_timeout_seconds: 1
|
157
|
+
# storage:
|
158
|
+
# meta_key_value_store:
|
159
|
+
# type: qdbm_depot
|
160
|
+
# configuration:
|
161
|
+
# bnum 1200000
|
162
|
+
# use_checksum: true
|
163
|
+
# text_key_value_store:
|
164
|
+
# type: qdbm_curia
|
165
|
+
# configuration_file: text_kvs_config.yml
|
166
|
+
# use_checksum: true
|
167
|
+
# authentication:
|
168
|
+
# hostname: imap.example.com
|
169
|
+
# password_sources:
|
170
|
+
# - type: plain
|
171
|
+
# configuration:
|
172
|
+
# - user: alice
|
173
|
+
# pass: open sesame
|
174
|
+
# - user: bob
|
175
|
+
# pass: Z1ON0101
|
176
|
+
# - type: hash
|
177
|
+
# configuration_file: passwd_hash.yml
|
178
|
+
# - type: ldap
|
179
|
+
# configuration:
|
180
|
+
# ldap_uri: ldap://ldap.example.com/ou=user,o=example,dc=nodomain?uid?one?(memberOf=cn=imap,ou=group,o=example,dc=nodomain)
|
181
|
+
# authorization:
|
182
|
+
# mail_delivery_user: "#postman"
|
183
|
+
#
|
184
|
+
# backward compatibility for required_features.
|
185
|
+
# load_libraries:
|
186
|
+
# - rims/qdbm
|
187
|
+
# - rims/passwd/ldap
|
188
|
+
#
|
189
|
+
# backward compatibility for logging.
|
190
|
+
# log_file: rims.log
|
191
|
+
# log_level: debug
|
192
|
+
# log_shift_age: 10
|
193
|
+
# log_shift_size: 1048576
|
194
|
+
# log_stdout: info
|
195
|
+
#
|
196
|
+
# backward compatibility for daemon.
|
197
|
+
# process_privilege_user: nobody
|
198
|
+
# process_privilege_group: nogroup
|
199
|
+
#
|
200
|
+
# backward compatibility for server.
|
201
|
+
# imap_host: 0.0.0.0
|
202
|
+
# imap_port: 143
|
203
|
+
# ip_addr: 0.0.0.0
|
204
|
+
# ip_port: 143
|
205
|
+
# send_buffer_limit: 16384
|
206
|
+
#
|
207
|
+
# backward compatibility for lock.
|
208
|
+
# read_lock_timeout_seconds: 30
|
209
|
+
# write_lock_timeout_seconds: 30
|
210
|
+
# cleanup_write_lock_timeout_seconds: 1
|
211
|
+
#
|
212
|
+
# backward compatibility for storage.
|
213
|
+
# key_value_store_type: gdbm
|
214
|
+
# use_key_value_store_checksum: true
|
215
|
+
# meta_key_value_store:
|
216
|
+
# plug_in: qdbm_depot
|
217
|
+
# configuration:
|
218
|
+
# bnum 1200000
|
219
|
+
# use_checksum: true
|
220
|
+
# text_key_value_store:
|
221
|
+
# plug_in: qdbm_curia
|
222
|
+
# configuration_file: text_kvs_config.yml
|
223
|
+
# use_checksum: true
|
224
|
+
#
|
225
|
+
# backward compatibility for authentication.
|
226
|
+
# hostname: imap.example.com
|
227
|
+
# username: alice
|
228
|
+
# password: open sesame
|
229
|
+
# user_list:
|
230
|
+
# - user: alice
|
231
|
+
# pass: open sesame
|
232
|
+
# - user: bob
|
233
|
+
# pass: Z1ON0101
|
234
|
+
# authentication:
|
235
|
+
# - plug_in: plain
|
236
|
+
# configuration:
|
237
|
+
# - user: alice
|
238
|
+
# pass: open sesame
|
239
|
+
# - user: bob
|
240
|
+
# pass: Z1ON0101
|
241
|
+
# - plug_in: hash
|
242
|
+
# configuration_file: passwd_hash.yml
|
243
|
+
# - plug_in: ldap
|
244
|
+
# configuration:
|
245
|
+
# ldap_uri: ldap://ldap.example.com/ou=user,o=example,dc=nodomain?uid?one?(memberOf=cn=imap,ou=group,o=example,dc=nodomain)
|
246
|
+
#
|
247
|
+
# backward compatibility for authorization.
|
248
|
+
# mail_delivery_user: "#postman"
|
249
|
+
#
|
250
|
+
def load_yaml(path)
|
251
|
+
load(YAML.load_file(path), File.dirname(path))
|
252
|
+
self
|
253
|
+
end
|
254
|
+
|
255
|
+
def require_features
|
256
|
+
# built-in plug-in
|
257
|
+
require 'rims/gdbm_kvs'
|
258
|
+
require 'rims/passwd'
|
259
|
+
|
260
|
+
if (feature_list = get_required_features) then
|
261
|
+
for feature in feature_list
|
262
|
+
require(feature)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_required_features
|
270
|
+
@config.dig('required_features') ||
|
271
|
+
@config.dig('load_libraries') || # for backward compatibility
|
272
|
+
[]
|
273
|
+
end
|
274
|
+
|
275
|
+
def base_dir
|
276
|
+
@config['base_dir'] or raise KeyError, 'not defined base_dir.'
|
277
|
+
end
|
278
|
+
|
279
|
+
def get_configuration(collection)
|
280
|
+
if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
|
281
|
+
self.class.get_configuration(collection, base_dir)
|
282
|
+
else
|
283
|
+
self.class.get_configuration(collection)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
private :get_configuration
|
287
|
+
|
288
|
+
# return parameters for Logger.new
|
289
|
+
def make_file_logger_params
|
290
|
+
log_path = Pathname(@config.dig('logging', 'file', 'path') ||
|
291
|
+
@config.dig('log_file') || # for backward compatibility
|
292
|
+
'rims.log')
|
293
|
+
if (log_path.relative?) then
|
294
|
+
if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
|
295
|
+
log_path = base_dir + log_path
|
296
|
+
end
|
297
|
+
end
|
298
|
+
logger_params = [ log_path.to_s ]
|
299
|
+
|
300
|
+
shift_age = @config.dig('logging', 'file', 'shift_age') ||
|
301
|
+
@config.dig('log_shift_age') # for backward compatibility
|
302
|
+
shift_size = @config.dig('logging', 'file', 'shift_size') ||
|
303
|
+
@config.dig('log_shift_size') # for backward compatibility
|
304
|
+
if (shift_size) then
|
305
|
+
logger_params << (shift_age || 0)
|
306
|
+
logger_params << shift_size
|
307
|
+
elsif (shift_age) then
|
308
|
+
logger_params << shift_age
|
309
|
+
end
|
310
|
+
|
311
|
+
kw_args = {}
|
312
|
+
kw_args[:level] = @config.dig('logging', 'file', 'level') ||
|
313
|
+
@config.dig('log_level') || # for backward compatibility
|
314
|
+
'info'
|
315
|
+
kw_args[:progname] = 'rims'
|
316
|
+
if (datetime_format = @config.dig('logging', 'file', 'datetime_format')) then
|
317
|
+
kw_args[:datetime_format] = datetime_format
|
318
|
+
end
|
319
|
+
if (shift_period_suffix = @config.dig('logging', 'file', 'shift_period_suffix')) then
|
320
|
+
kw_args[:shift_period_suffix] = shift_period_suffix
|
321
|
+
end
|
322
|
+
logger_params << kw_args
|
323
|
+
|
324
|
+
logger_params
|
325
|
+
end
|
326
|
+
|
327
|
+
# return parameters for Logger.new
|
328
|
+
def make_stdout_logger_params
|
329
|
+
logger_params = [ STDOUT ]
|
330
|
+
|
331
|
+
kw_args = {}
|
332
|
+
kw_args[:level] = @config.dig('logging', 'stdout', 'level') ||
|
333
|
+
@config.dig('log_stdout') || # for backward compatibility
|
334
|
+
'info'
|
335
|
+
kw_args[:progname] = 'rims'
|
336
|
+
if (datetime_format = @config.dig('logging', 'stdout', 'datetime_format')) then
|
337
|
+
kw_args[:datetime_format] = datetime_format
|
338
|
+
end
|
339
|
+
logger_params << kw_args
|
340
|
+
|
341
|
+
logger_params
|
342
|
+
end
|
343
|
+
|
344
|
+
# return parameters for Logger.new
|
345
|
+
def make_protocol_logger_params
|
346
|
+
log_path = Pathname(@config.dig('logging', 'protocol', 'path') || 'protocol.log')
|
347
|
+
if (log_path.relative?) then
|
348
|
+
if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
|
349
|
+
log_path = base_dir + log_path
|
350
|
+
end
|
351
|
+
end
|
352
|
+
logger_params = [ log_path.to_s ]
|
353
|
+
|
354
|
+
shift_age = @config.dig('logging', 'protocol', 'shift_age')
|
355
|
+
shift_size = @config.dig('logging', 'protocol', 'shift_size')
|
356
|
+
if (shift_size) then
|
357
|
+
logger_params << (shift_age || 0)
|
358
|
+
logger_params << shift_size
|
359
|
+
elsif (shift_age) then
|
360
|
+
logger_params << shift_age
|
361
|
+
end
|
362
|
+
|
363
|
+
kw_args = {}
|
364
|
+
kw_args[:level] = @config.dig('logging', 'protocol', 'level') || 'unknown'
|
365
|
+
kw_args[:progname] = 'rims'
|
366
|
+
if (datetime_format = @config.dig('logging', 'protocol', 'datetime_format')) then
|
367
|
+
kw_args[:datetime_format] = datetime_format
|
368
|
+
end
|
369
|
+
if (shift_period_suffix = @config.dig('logging', 'protocol', 'shift_period_suffix')) then
|
370
|
+
kw_args[:shift_period_suffix] = shift_period_suffix
|
371
|
+
end
|
372
|
+
logger_params << kw_args
|
373
|
+
|
374
|
+
logger_params
|
375
|
+
end
|
376
|
+
|
377
|
+
def daemonize?
|
378
|
+
daemon_config = @config['daemon'] || {}
|
379
|
+
if (daemon_config.key? 'daemonize') then
|
380
|
+
daemon_config['daemonize']
|
381
|
+
else
|
382
|
+
true
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def daemon_name
|
387
|
+
'rims'
|
388
|
+
end
|
389
|
+
|
390
|
+
def daemon_debug?
|
391
|
+
daemon_config = @config['daemon'] || {}
|
392
|
+
if (daemon_config.key? 'debug') then
|
393
|
+
daemon_config['debug']
|
394
|
+
else
|
395
|
+
false
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def status_file
|
400
|
+
file_path = @config.dig('daemon', 'status_file') || 'rims.pid'
|
401
|
+
file_path = Pathname(file_path)
|
402
|
+
if (file_path.relative?) then
|
403
|
+
if (@config.key? 'base_dir') then # to avoid an error with DEFAULT_CONFIG
|
404
|
+
file_path = base_dir + file_path
|
405
|
+
end
|
406
|
+
end
|
407
|
+
file_path.to_s
|
408
|
+
end
|
409
|
+
|
410
|
+
def server_polling_interval_seconds
|
411
|
+
@config.dig('daemon', 'server_polling_interval_seconds') || 3
|
412
|
+
end
|
413
|
+
|
414
|
+
def server_restart_overlap_seconds
|
415
|
+
# to avoid resource conflict between the new server and the old server.
|
416
|
+
0
|
417
|
+
end
|
418
|
+
|
419
|
+
def server_privileged_user
|
420
|
+
@config.dig('daemon', 'server_privileged_user') ||
|
421
|
+
@config.dig('process_privilege_user') # for backward compatibility
|
422
|
+
end
|
423
|
+
|
424
|
+
def server_privileged_group
|
425
|
+
@config.dig('daemon', 'server_privileged_group') ||
|
426
|
+
@config.dig('process_privilege_group') # for backward compatibility
|
427
|
+
end
|
428
|
+
|
429
|
+
def listen_address
|
430
|
+
if (socket_address = @config.dig('server', 'listen_address')) then
|
431
|
+
return socket_address
|
432
|
+
end
|
433
|
+
|
434
|
+
# for backward compatibility
|
435
|
+
host = @config.dig('imap_host') || @config.dig('ip_addr')
|
436
|
+
port = @config.dig('imap_port') || @config.dig('ip_port')
|
437
|
+
if (host || port) then
|
438
|
+
socket_adress = {
|
439
|
+
'type' => 'tcp',
|
440
|
+
'host' => host || '0.0.0.0',
|
441
|
+
'port' => port || 1430
|
442
|
+
}
|
443
|
+
return socket_adress
|
444
|
+
end
|
445
|
+
|
446
|
+
# default
|
447
|
+
'0.0.0.0:1430'
|
448
|
+
end
|
449
|
+
|
450
|
+
def accept_polling_timeout_seconds
|
451
|
+
@config.dig('server', 'accept_polling_timeout_seconds') || 0.1
|
452
|
+
end
|
453
|
+
|
454
|
+
def process_num
|
455
|
+
# not yet supported multi-process server configuration.
|
456
|
+
0
|
457
|
+
end
|
458
|
+
|
459
|
+
def process_queue_size
|
460
|
+
20
|
461
|
+
end
|
462
|
+
|
463
|
+
def process_queue_polling_timeout_seconds
|
464
|
+
0.1
|
465
|
+
end
|
466
|
+
|
467
|
+
def process_send_io_polling_timeout_seconds
|
468
|
+
0.1
|
469
|
+
end
|
470
|
+
|
471
|
+
def thread_num
|
472
|
+
@config.dig('server', 'thread_num') || 20
|
473
|
+
end
|
474
|
+
|
475
|
+
def thread_queue_size
|
476
|
+
@config.dig('server', 'thread_queue_size') || 20
|
477
|
+
end
|
478
|
+
|
479
|
+
def thread_queue_polling_timeout_seconds
|
480
|
+
@config.dig('server', 'thread_queue_polling_timeout_seconds') || 0.1
|
481
|
+
end
|
482
|
+
|
483
|
+
def send_buffer_limit_size
|
484
|
+
@config.dig('server', 'send_buffer_limit_size') ||
|
485
|
+
@config.dig('send_buffer_limit') || # for backward compatibility
|
486
|
+
1024 * 16
|
487
|
+
end
|
488
|
+
|
489
|
+
module SSLContextConfigAttribute
|
490
|
+
def ssl_context
|
491
|
+
@__ssl_context__
|
492
|
+
end
|
493
|
+
|
494
|
+
def base_dir
|
495
|
+
@__base_dir__
|
496
|
+
end
|
497
|
+
|
498
|
+
alias _ ssl_context
|
499
|
+
|
500
|
+
class << self
|
501
|
+
def new_module(ssl_context, base_dir)
|
502
|
+
_module = Module.new
|
503
|
+
_module.instance_variable_set(:@__ssl_context__, ssl_context)
|
504
|
+
_module.instance_variable_set(:@__base_dir__, base_dir)
|
505
|
+
_module.module_eval{
|
506
|
+
include OpenSSL
|
507
|
+
include OpenSSL::SSL
|
508
|
+
extend SSLContextConfigAttribute
|
509
|
+
}
|
510
|
+
_module
|
511
|
+
end
|
512
|
+
|
513
|
+
# methodized to isolate local variable scope.
|
514
|
+
def eval_config(_module, expr, filename='(eval_config)')
|
515
|
+
_module.module_eval(expr, filename)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def ssl_context
|
521
|
+
if (openssl_config = @config['openssl']) then
|
522
|
+
if (openssl_config.key? 'use_ssl') then
|
523
|
+
use_ssl = openssl_config['use_ssl']
|
524
|
+
else
|
525
|
+
use_ssl = openssl_config.key? 'ssl_context'
|
526
|
+
end
|
527
|
+
|
528
|
+
if (use_ssl) then
|
529
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
530
|
+
if (ssl_config_expr = openssl_config['ssl_context']) then
|
531
|
+
anon_mod = SSLContextConfigAttribute.new_module(ssl_context, base_dir)
|
532
|
+
SSLContextConfigAttribute.eval_config(anon_mod, ssl_config_expr, 'ssl_context')
|
533
|
+
end
|
534
|
+
|
535
|
+
ssl_context
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
def read_lock_timeout_seconds
|
541
|
+
@config.dig('lock', 'read_lock_timeout_seconds') ||
|
542
|
+
@config.dig('read_lock_timeout_seconds') || # for backward compatibility
|
543
|
+
30
|
544
|
+
end
|
545
|
+
|
546
|
+
def write_lock_timeout_seconds
|
547
|
+
@config.dig('lock', 'write_lock_timeout_seconds') ||
|
548
|
+
@config.dig('write_lock_timeout_seconds') || # for backward compatibility
|
549
|
+
30
|
550
|
+
end
|
551
|
+
|
552
|
+
def cleanup_write_lock_timeout_seconds
|
553
|
+
@config.dig('lock', 'cleanup_write_lock_timeout_seconds') ||
|
554
|
+
@config.dig('cleanup_write_lock_timeout_seconds') || # for backward compatibility
|
555
|
+
1
|
556
|
+
end
|
557
|
+
|
558
|
+
KeyValueStoreFactoryBuilderParams = Struct.new(:origin_type, :origin_config, :middleware_list)
|
559
|
+
class KeyValueStoreFactoryBuilderParams
|
560
|
+
def build_factory
|
561
|
+
builder = KeyValueStore::FactoryBuilder.new
|
562
|
+
builder.open{|name| origin_type.open_with_conf(name, origin_config) }
|
563
|
+
for middleware in middleware_list
|
564
|
+
builder.use(middleware)
|
565
|
+
end
|
566
|
+
builder.factory
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
def make_key_value_store_params(collection)
|
571
|
+
kvs_params = KeyValueStoreFactoryBuilderParams.new
|
572
|
+
kvs_params.origin_type = KeyValueStore::FactoryBuilder.get_plug_in(collection['type'] ||
|
573
|
+
collection['plug_in'] || # for backward compatibility
|
574
|
+
@config['key_value_store_type'] || # for backward compatibility
|
575
|
+
'gdbm')
|
576
|
+
kvs_params.origin_config = get_configuration(collection)
|
577
|
+
|
578
|
+
if (collection.key? 'use_checksum') then
|
579
|
+
use_checksum = collection['use_checksum']
|
580
|
+
elsif (@config.key? 'use_key_value_store_checksum') then
|
581
|
+
use_checksum = @config['use_key_value_store_checksum'] # for backward compatibility
|
582
|
+
else
|
583
|
+
use_checksum = true # default
|
584
|
+
end
|
585
|
+
|
586
|
+
kvs_params.middleware_list = []
|
587
|
+
kvs_params.middleware_list << Checksum_KeyValueStore if use_checksum
|
588
|
+
|
589
|
+
kvs_params
|
590
|
+
end
|
591
|
+
private :make_key_value_store_params
|
592
|
+
|
593
|
+
def make_meta_key_value_store_params
|
594
|
+
make_key_value_store_params(@config.dig('storage', 'meta_key_value_store') ||
|
595
|
+
@config.dig('meta_key_value_store') || # for backward compatibility
|
596
|
+
{})
|
597
|
+
end
|
598
|
+
|
599
|
+
def make_text_key_value_store_params
|
600
|
+
make_key_value_store_params(@config.dig('storage', 'text_key_value_store') ||
|
601
|
+
@config.dig('text_key_value_store') || # for backward compatibility
|
602
|
+
{})
|
603
|
+
end
|
604
|
+
|
605
|
+
def make_key_value_store_path(mailbox_data_structure_version, unique_user_id)
|
606
|
+
if (mailbox_data_structure_version.empty?) then
|
607
|
+
raise ArgumentError, 'too short mailbox data structure version.'
|
608
|
+
end
|
609
|
+
if (unique_user_id.length <= 2) then
|
610
|
+
raise ArgumentError, 'too short unique user ID.'
|
611
|
+
end
|
612
|
+
|
613
|
+
bucket_dir_name = unique_user_id[0..1]
|
614
|
+
store_dir_name = unique_user_id[2..-1]
|
615
|
+
|
616
|
+
base_dir + mailbox_data_structure_version + bucket_dir_name + store_dir_name
|
617
|
+
end
|
618
|
+
|
619
|
+
def make_authentication
|
620
|
+
if ((@config.key? 'authentication') && (@config['authentication'].is_a? Hash)) then
|
621
|
+
auth_conf = @config['authentication']
|
622
|
+
else
|
623
|
+
auth_conf = {}
|
624
|
+
end
|
625
|
+
|
626
|
+
hostname = auth_conf['hostname'] ||
|
627
|
+
@config['hostname'] || # for backward compatibility
|
628
|
+
Socket.gethostname
|
629
|
+
auth = Authentication.new(hostname: hostname)
|
630
|
+
|
631
|
+
if (passwd_src_list = auth_conf['password_sources']) then
|
632
|
+
for passwd_src_conf in passwd_src_list
|
633
|
+
plug_in_name = passwd_src_conf['type'] or raise KeyError, 'not found a password source type.'
|
634
|
+
plug_in_config = get_configuration(passwd_src_conf)
|
635
|
+
passwd_src = Authentication.get_plug_in(plug_in_name, plug_in_config)
|
636
|
+
auth.add_plug_in(passwd_src)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
# for backward compatibility
|
641
|
+
if (user_list = @config['user_list']) then
|
642
|
+
plain_src = Password::PlainSource.new
|
643
|
+
for pw in user_list
|
644
|
+
user = pw['user'] or raise KeyError, 'not found a user_list user.'
|
645
|
+
pass = pw['pass'] or raise KeyError, 'not found a user_list pass.'
|
646
|
+
plain_src.entry(user, pass)
|
647
|
+
end
|
648
|
+
auth.add_plug_in(plain_src)
|
649
|
+
end
|
650
|
+
|
651
|
+
# for backward compatibility
|
652
|
+
if (username = @config['username']) then
|
653
|
+
password = @config['password'] or raise KeyError, 'not found a password.'
|
654
|
+
plain_src = Password::PlainSource.new
|
655
|
+
plain_src.entry(username, password)
|
656
|
+
auth.add_plug_in(plain_src)
|
657
|
+
end
|
658
|
+
|
659
|
+
# for backward compatibility
|
660
|
+
if ((@config.key? 'authentication') && (@config['authentication'].is_a? Array)) then
|
661
|
+
plug_in_list = @config['authentication']
|
662
|
+
for plug_in_conf in plug_in_list
|
663
|
+
plug_in_name = plug_in_conf['plug_in'] or raise KeyError, 'not found an authentication plug_in.'
|
664
|
+
plug_in_config = get_configuration(plug_in_conf)
|
665
|
+
passwd_src = Authentication.get_plug_in(plug_in_name, plug_in_config)
|
666
|
+
auth.add_plug_in(passwd_src)
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
auth
|
671
|
+
end
|
672
|
+
|
673
|
+
def mail_delivery_user
|
674
|
+
@config.dig('authorization', 'mail_delivery_user') ||
|
675
|
+
@config.dig('mail_delivery_user') || # for backward compatibility
|
676
|
+
'#postman'
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
DEFAULT_CONFIG = Configuration.new
|
681
|
+
|
682
|
+
# default features
|
683
|
+
DEFAULT_CONFIG.require_features
|
684
|
+
|
685
|
+
# for read-only
|
686
|
+
class << DEFAULT_CONFIG
|
687
|
+
undef load
|
688
|
+
undef load_yaml
|
689
|
+
end
|
690
|
+
|
691
|
+
def initialize(config)
|
692
|
+
@config = config
|
693
|
+
end
|
694
|
+
|
695
|
+
using Logger::JointPlus
|
696
|
+
|
697
|
+
def setup(server, daemon: false)
|
698
|
+
file_logger_params = @config.make_file_logger_params
|
699
|
+
logger = Logger.new(*file_logger_params)
|
700
|
+
|
701
|
+
stdout_logger_params = @config.make_stdout_logger_params
|
702
|
+
unless (daemon && @config.daemonize?) then
|
703
|
+
logger += Logger.new(*stdout_logger_params)
|
704
|
+
end
|
705
|
+
|
706
|
+
protocol_logger_params = @config.make_protocol_logger_params
|
707
|
+
protocol_logger = Logger.new(*protocol_logger_params)
|
708
|
+
|
709
|
+
logger.info('preload libraries.')
|
710
|
+
Riser.preload
|
711
|
+
Riser.preload(RIMS)
|
712
|
+
Riser.preload(RIMS::Protocol)
|
713
|
+
@config.require_features
|
714
|
+
|
715
|
+
logger.info('setup server.')
|
716
|
+
server.accept_polling_timeout_seconds = @config.accept_polling_timeout_seconds
|
717
|
+
server.process_num = @config.process_num
|
718
|
+
server.process_queue_size = @config.process_queue_size
|
719
|
+
server.process_queue_polling_timeout_seconds = @config.process_queue_polling_timeout_seconds
|
720
|
+
server.process_send_io_polling_timeout_seconds = @config.process_send_io_polling_timeout_seconds
|
721
|
+
server.thread_num = @config.thread_num
|
722
|
+
server.thread_queue_size = @config.thread_queue_size
|
723
|
+
server.thread_queue_polling_timeout_seconds = @config.thread_queue_polling_timeout_seconds
|
724
|
+
|
725
|
+
ssl_context = @config.ssl_context
|
726
|
+
|
727
|
+
make_kvs_factory = lambda{|kvs_params, kvs_type|
|
728
|
+
kvs_factory = kvs_params.build_factory
|
729
|
+
return lambda{|mailbox_data_structure_version, unique_user_id, db_name|
|
730
|
+
kvs_path = @config.make_key_value_store_path(mailbox_data_structure_version, unique_user_id)
|
731
|
+
unless (kvs_path.directory?) then
|
732
|
+
logger.debug("make a directory: #{kvs_path}") if logger.debug?
|
733
|
+
kvs_path.mkpath
|
734
|
+
end
|
735
|
+
db_path = kvs_path + db_name
|
736
|
+
logger.debug("#{kvs_type} data key-value sotre path: #{db_path}") if logger.debug?
|
737
|
+
kvs_factory.call(db_path.to_s)
|
738
|
+
}, lambda{
|
739
|
+
logger.info("#{kvs_type} key-value store parameter: type=#{kvs_params.origin_type}")
|
740
|
+
logger.info("#{kvs_type} key-value store parameter: config=#{kvs_params.origin_config.to_json}")
|
741
|
+
kvs_params.middleware_list.each_with_index do |middleware, i|
|
742
|
+
logger.info("#{kvs_type} key-value store parameter: middleware[#{i}]=#{middleware}")
|
743
|
+
end
|
744
|
+
}
|
745
|
+
}
|
746
|
+
|
747
|
+
kvs_meta_open, kvs_meta_log = make_kvs_factory.call(@config.make_meta_key_value_store_params, 'meta')
|
748
|
+
kvs_text_open, kvs_text_log = make_kvs_factory.call(@config.make_text_key_value_store_params, 'text')
|
749
|
+
auth = @config.make_authentication
|
750
|
+
mail_store_pool = MailStore.build_pool(kvs_meta_open, kvs_text_open)
|
751
|
+
|
752
|
+
server.before_start{|server_socket|
|
753
|
+
logger.info('start server.')
|
754
|
+
for feature in @config.get_required_features
|
755
|
+
logger.info("required feature: #{feature}")
|
756
|
+
end
|
757
|
+
logger.info("file logging parameter: path=#{file_logger_params[0]}")
|
758
|
+
file_logger_params[1..-2].each_with_index do |value, i|
|
759
|
+
logger.info("file logging parameter: shift_args[#{i}]=#{value}")
|
760
|
+
end
|
761
|
+
for name, value in file_logger_params[-1]
|
762
|
+
logger.info("file logging parameter: #{name}=#{value}")
|
763
|
+
end
|
764
|
+
for name, value in stdout_logger_params[-1]
|
765
|
+
logger.info("stdout logging parameter: #{name}=#{value}")
|
766
|
+
end
|
767
|
+
logger.info("protocol logging parameter: path=#{protocol_logger_params[0]}")
|
768
|
+
protocol_logger_params[1..-2].each_with_index do |value, i|
|
769
|
+
logger.info("protocol logging parameter: shift_args[#{i}]=#{value}")
|
770
|
+
end
|
771
|
+
for name, value in protocol_logger_params[-1]
|
772
|
+
logger.info("protocol logging parameter: #{name}=#{value}")
|
773
|
+
end
|
774
|
+
logger.info("listen address: #{server_socket.local_address.inspect_sockaddr}")
|
775
|
+
privileged_user = Etc.getpwuid(Process.euid).name rescue ''
|
776
|
+
logger.info("server privileged user: #{privileged_user}(#{Process.euid})")
|
777
|
+
privileged_group = Etc.getgrgid(Process.egid).name rescue ''
|
778
|
+
logger.info("server privileged group: #{privileged_group}(#{Process.egid})")
|
779
|
+
logger.info("server parameter: accept_polling_timeout_seconds=#{server.accept_polling_timeout_seconds}")
|
780
|
+
logger.info("server parameter: process_num=#{server.process_num}")
|
781
|
+
logger.info("server parameter: process_queue_size=#{server.process_queue_size}")
|
782
|
+
logger.info("server parameter: process_queue_polling_timeout_seconds=#{server.process_queue_polling_timeout_seconds}")
|
783
|
+
logger.info("server parameter: process_send_io_polling_timeout_seconds=#{server.process_send_io_polling_timeout_seconds}")
|
784
|
+
logger.info("server parameter: thread_num=#{server.thread_num}")
|
785
|
+
logger.info("server parameter: thread_queue_size=#{server.thread_queue_size}")
|
786
|
+
logger.info("server parameter: thread_queue_polling_timeout_seconds=#{server.thread_queue_polling_timeout_seconds}")
|
787
|
+
logger.info("server parameter: send_buffer_limit_size=#{@config.send_buffer_limit_size}")
|
788
|
+
if (ssl_context) then
|
789
|
+
Array(ssl_context.alpn_protocols).each_with_index do |protocol, i|
|
790
|
+
logger.info("openssl parameter: alpn_protocols[#{i}]=#{protocol}")
|
791
|
+
end
|
792
|
+
logger.info("openssl parameter: alpn_select_cb=#{ssl_context.alpn_select_cb.inspect}") if ssl_context.alpn_select_cb
|
793
|
+
logger.info("openssl parameter: ca_file=#{ssl_context.ca_file}") if ssl_context.ca_file
|
794
|
+
logger.info("openssl parameter: ca_path=#{ssl_context.ca_path}") if ssl_context.ca_path
|
795
|
+
if (ssl_context.cert) then
|
796
|
+
ssl_context.cert.to_text.each_line do |line|
|
797
|
+
logger.info("openssl parameter: [cert] #{line.chomp}")
|
798
|
+
end
|
799
|
+
else
|
800
|
+
logger.warn('openssl parameter: not defined cert attribute.')
|
801
|
+
end
|
802
|
+
logger.info("openssl parameter: cert_store=#{ssl_context.cert_store.inspect}") if ssl_context.cert_store
|
803
|
+
Array(ssl_context.ciphers).each_with_index do |cipher, i|
|
804
|
+
logger.info("openssl parameter: ciphers[#{i}]=#{cipher.join(',')}")
|
805
|
+
end
|
806
|
+
Array(ssl_context.client_ca).each_with_index do |cert, i|
|
807
|
+
cert.to_text.each_line do |line|
|
808
|
+
logger.info("openssl parameter: client_ca[#{i}]: #{line.chomp}")
|
809
|
+
end
|
810
|
+
end
|
811
|
+
logger.info("openssl parameter: client_cert_cb=#{ssl_context.client_cert_cb.inspect}") if ssl_context.client_cert_cb
|
812
|
+
Array(ssl_context.extra_chain_cert).each_with_index do |cert, i|
|
813
|
+
cert.to_text.each_line do |line|
|
814
|
+
logger.info("openssl parameter: extra_chain_cert[#{i}]: #{line.chomp}")
|
815
|
+
end
|
816
|
+
end
|
817
|
+
if (ssl_context.key) then
|
818
|
+
logger.info("openssl parameter: key=#{ssl_context.key.inspect}")
|
819
|
+
if (logger.debug?) then
|
820
|
+
ssl_context.key.to_text.each_line do |line|
|
821
|
+
logger.debug("openssl parameter: [key] #{line.chomp}")
|
822
|
+
end
|
823
|
+
end
|
824
|
+
else
|
825
|
+
logger.warn('openssl parameter: not defined key attribute.')
|
826
|
+
end
|
827
|
+
Array(ssl_context.npn_protocols).each_with_index do |protocol, i|
|
828
|
+
logger.info("openssl parameter: npn_protocols[#{i}]=#{protocol}")
|
829
|
+
end
|
830
|
+
logger.info("openssl parameter: npn_select_cb=#{ssl_context.npn_select_cb.inspect}") if ssl_context.npn_select_cb
|
831
|
+
logger.info("openssl parameter: options=0x#{'%08x' % ssl_context.options}") if ssl_context.options
|
832
|
+
logger.info("openssl parameter: renegotiation_cb=#{ssl_context.renegotiation_cb.inspect}") if ssl_context.renegotiation_cb
|
833
|
+
logger.info("openssl parameter: security_level=#{ssl_context.security_level}")
|
834
|
+
logger.info("openssl parameter: servername_cb=#{ssl_context.servername_cb.inspect}") if ssl_context.servername_cb
|
835
|
+
logger.info("openssl parameter: session_cache_mode=0x#{'%08x' % ssl_context.session_cache_mode}")
|
836
|
+
logger.info("openssl parameter: session_cache_size=#{ssl_context.session_cache_size }")
|
837
|
+
logger.info("openssl parameter: session_get_cb=#{ssl_context.session_get_cb.inspect}") if ssl_context.session_get_cb
|
838
|
+
logger.info("openssl parameter: session_id_context=#{ssl_context.session_id_context}") if ssl_context.session_id_context
|
839
|
+
logger.info("openssl parameter: session_new_cb=#{ssl_context.session_new_cb.inspect}") if ssl_context.session_new_cb
|
840
|
+
logger.info("openssl parameter: session_remove_cb=#{ssl_context.session_remove_cb}") if ssl_context.session_remove_cb
|
841
|
+
logger.info("openssl parameter: ssl_timeout=#{ssl_context.ssl_timeout}") if ssl_context.ssl_timeout
|
842
|
+
logger.info("openssl parameter: tmp_dh_callback=#{ssl_context.tmp_dh_callback}") if ssl_context.tmp_dh_callback
|
843
|
+
logger.info("openssl parameter: verify_callback=#{ssl_context.verify_callback}") if ssl_context.verify_callback
|
844
|
+
logger.info("openssl parameter: verify_depth=#{ssl_context.verify_depth}") if ssl_context.verify_depth
|
845
|
+
logger.info("openssl parameter: verify_hostname=#{ssl_context.verify_hostname}") if ssl_context.verify_hostname
|
846
|
+
logger.info("openssl parameter: verify_mode=0x#{'%08x' % ssl_context.verify_mode}") if ssl_context.verify_mode
|
847
|
+
end
|
848
|
+
logger.info("lock parameter: read_lock_timeout_seconds=#{@config.read_lock_timeout_seconds}")
|
849
|
+
logger.info("lock parameter: write_lock_timeout_seconds=#{@config.write_lock_timeout_seconds}")
|
850
|
+
logger.info("lock parameter: cleanup_write_lock_timeout_seconds=#{@config.cleanup_write_lock_timeout_seconds}")
|
851
|
+
kvs_meta_log.call
|
852
|
+
kvs_text_log.call
|
853
|
+
logger.info("authentication parameter: hostname=#{auth.hostname}")
|
854
|
+
logger.info("authorization parameter: mail_delivery_user=#{@config.mail_delivery_user}")
|
855
|
+
}
|
856
|
+
# server.at_fork{}
|
857
|
+
# server.at_stop{}
|
858
|
+
server.at_stat{|info|
|
859
|
+
logger.info("stat: #{info.to_json}")
|
860
|
+
}
|
861
|
+
server.preprocess{
|
862
|
+
auth.start_plug_in(logger)
|
863
|
+
}
|
864
|
+
server.dispatch{|socket|
|
865
|
+
begin
|
866
|
+
begin
|
867
|
+
begin
|
868
|
+
begin
|
869
|
+
remote_address = socket.remote_address # the place where the remote socket is most likely not closed is here
|
870
|
+
logger.info("accept connection: #{remote_address.inspect_sockaddr}")
|
871
|
+
if (ssl_context) then
|
872
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
|
873
|
+
logger.info("start tls: #{ssl_socket.state}")
|
874
|
+
ssl_socket.accept
|
875
|
+
logger.info("accept tls: #{ssl_socket.state}")
|
876
|
+
ssl_socket.sync = true
|
877
|
+
stream = Riser::WriteBufferStream.new(ssl_socket, @config.send_buffer_limit_size)
|
878
|
+
else
|
879
|
+
stream = Riser::WriteBufferStream.new(socket, @config.send_buffer_limit_size)
|
880
|
+
end
|
881
|
+
stream = Riser::LoggingStream.new(stream, protocol_logger)
|
882
|
+
decoder = Protocol::Decoder.new_decoder(mail_store_pool, auth, logger,
|
883
|
+
mail_delivery_user: @config.mail_delivery_user,
|
884
|
+
read_lock_timeout_seconds: @config.read_lock_timeout_seconds,
|
885
|
+
write_lock_timeout_seconds: @config.write_lock_timeout_seconds,
|
886
|
+
cleanup_write_lock_timeout_seconds: @config.cleanup_write_lock_timeout_seconds)
|
887
|
+
Protocol::Decoder.repl(decoder, stream, stream, logger)
|
888
|
+
ensure
|
889
|
+
if (stream) then
|
890
|
+
stream.flush
|
891
|
+
end
|
892
|
+
end
|
893
|
+
ensure
|
894
|
+
if (ssl_socket) then
|
895
|
+
ssl_socket.close
|
896
|
+
logger.info("close tls: #{ssl_socket.state}")
|
897
|
+
end
|
898
|
+
end
|
899
|
+
ensure
|
900
|
+
socket.close
|
901
|
+
if (remote_address) then
|
902
|
+
logger.info("close connection: #{remote_address.inspect_sockaddr}")
|
903
|
+
else
|
904
|
+
logger.info('close connection.')
|
905
|
+
end
|
906
|
+
end
|
907
|
+
rescue
|
908
|
+
logger.error('interrupt connection with unexpected error.')
|
909
|
+
Error.trace_error_chain($!) do |exception|
|
910
|
+
logger.error(exception)
|
911
|
+
end
|
912
|
+
end
|
913
|
+
}
|
914
|
+
server.postprocess{
|
915
|
+
auth.stop_plug_in(logger)
|
916
|
+
}
|
917
|
+
server.after_stop{
|
918
|
+
logger.info('stop server.')
|
919
|
+
}
|
920
|
+
|
921
|
+
nil
|
922
|
+
end
|
923
|
+
end
|
924
|
+
end
|
925
|
+
|
926
|
+
if ($0 == __FILE__) then
|
927
|
+
require 'pp' if $DEBUG
|
928
|
+
require 'rims'
|
929
|
+
|
930
|
+
if (ARGV.length != 1) then
|
931
|
+
STDERR.puts "usage: #{$0} config.yml"
|
932
|
+
exit(1)
|
933
|
+
end
|
934
|
+
|
935
|
+
config = RIMS::Service::Configuration.new
|
936
|
+
config.load_yaml(ARGV[0])
|
937
|
+
pp config if $DEBUG
|
938
|
+
|
939
|
+
server = Riser::SocketServer.new
|
940
|
+
service = RIMS::Service.new(config)
|
941
|
+
service.setup(server)
|
942
|
+
|
943
|
+
Signal.trap(:INT) { server.signal_stop_forced }
|
944
|
+
Signal.trap(:TERM) { server.signal_stop_graceful }
|
945
|
+
|
946
|
+
listen_address = Riser::SocketAddress.parse(config.listen_address)
|
947
|
+
server.start(listen_address.open_server)
|
948
|
+
end
|
949
|
+
|
950
|
+
# Local Variables:
|
951
|
+
# mode: Ruby
|
952
|
+
# indent-tabs-mode: nil
|
953
|
+
# End:
|