rims 0.2.2 → 0.2.3
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/.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:
|