rims 0.2.8 → 0.2.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba592c87ab4cce58d5f6682fae4a4cf446d441cb2e308fdab045661522668923
4
- data.tar.gz: d5d1a23220a7fc458e63d4f496b02a765d89b341c895214d6acf5fae4175afa5
3
+ metadata.gz: d256a00569dbf130c60b1ce0a2e81eaaaee80c8f05bd539951044050d8730ed8
4
+ data.tar.gz: dadb9184722598429f498a3c024bb911717f2cacd28e8b2b72073704401c6af1
5
5
  SHA512:
6
- metadata.gz: 1520dc133af84b45c2bd91853eb77c3338bad120c04c9b9b9b44be679d4275ef55bb5653f3ee18fd3cded962af947232f53d353902f0f209bef6c8d7342a86d5
7
- data.tar.gz: cc62bb64bbaac372157304a7f9f8ad816c99aefdd251a577c2db4bfb1b6357b0be6b1c1db166369bf729350ed6fe1ab5c81f5fcc12cd31781ddc835328a187f3
6
+ metadata.gz: d630fcd4aea66809516f8878d5b0f9511331c3afaf1fc71a6f0ac08f5a4329345868f5a5cf2665e54a9985dd7939c7b478da2c8f7cfe1a686603f621519cef75
7
+ data.tar.gz: 6a34c95ce5f6a6012da5e790326e2e991ecccc0cb186ac53fd8c6b2b1f829df0b41c8087fa567970dbf912295901ed4e4a83865117e1c564df90da43b7874807
@@ -0,0 +1,73 @@
1
+ Change Log
2
+ ==========
3
+
4
+ 0.2.9
5
+ -----
6
+ Released on 2019-12-12.
7
+
8
+ ### Added
9
+ - Add `umask(2)` configuration parameter. [#29](https://github.com/y10k/rims/issues/29)
10
+ - Add debug logging for conflicted subscriber error. This problem is
11
+ not solved because it does not reappear. [#28](https://github.com/y10k/rims/issues/28)
12
+
13
+ ### Changed
14
+ - Ready to Ruby 2.7. [#35](https://github.com/y10k/rims/issues/35)
15
+
16
+ ### Fixed
17
+ - Fix a bug of detached thread finishing a protocol decoder engine.
18
+ - Make bulk message size of inter-process communication not exceeding `DRb`'s `load_limit`.
19
+ [#30](https://github.com/y10k/rims/issues/30)
20
+ [#33](https://github.com/y10k/rims/issues/33)
21
+
22
+ 0.2.8
23
+ -----
24
+ Released on 2019-10-10.
25
+
26
+ 0.2.7
27
+ -----
28
+ Released on 2019-07-27.
29
+
30
+ 0.2.6
31
+ -----
32
+ Released on 2019-07-09.
33
+
34
+ 0.2.5
35
+ -----
36
+ Released on 2019-06-10.
37
+
38
+ 0.2.4
39
+ -----
40
+ Released on 2019-04-25.
41
+
42
+ 0.2.3
43
+ -----
44
+ Released on 2019-04-10.
45
+
46
+ 0.2.2
47
+ -----
48
+ Released on 2019-03-06.
49
+
50
+ 0.2.1
51
+ -----
52
+ Released on 2019-02-18.
53
+
54
+ 0.1.0
55
+ -----
56
+ Released on 2015-02-22.
57
+
58
+ 0.0.4
59
+ -----
60
+ Released on 2014-06-08.
61
+
62
+ 0.0.3
63
+ -----
64
+ Released on 2014-04-15.
65
+
66
+ 0.0.2
67
+ -----
68
+ Released on 2014-03-05.
69
+
70
+ 0.0.1
71
+ -----
72
+ Released on 2014-02-24.
73
+
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ 2019-12-12 TOKI Yoshinori <toki@freedom.ne.jp>
2
+
3
+ * ChangeLog: updates to this file have been stopped.
4
+ see CHANGELOG.md from now on.
5
+
1
6
  2019-10-10 TOKI Yoshinori <toki@freedom.ne.jp>
2
7
 
3
8
  * RIMS version 0.2.8 is released.
data/Rakefile CHANGED
@@ -28,14 +28,18 @@ Rake::RDocTask.new do |rd|
28
28
  rd.rdoc_files.include('lib/**/*.rb')
29
29
  end
30
30
 
31
+ rule '.html' => '.md' do |t|
32
+ sh "pandoc --from=markdown --to=html5 --standalone --self-contained --css=$HOME/.pandoc/github.css --output=#{t.name} #{t.source}"
33
+ end
34
+
31
35
  desc 'Build README.html from markdown source'
32
36
  task :readme => %w[ README.html ]
33
-
34
- file 'README.html' => [ 'README.md' ] do
35
- sh "pandoc --from=markdown --to=html5 --standalone --self-contained --css=$HOME/.pandoc/github.css --output=README.html README.md"
36
- end
37
37
  CLOBBER.include 'README.html'
38
38
 
39
+ desc 'Build CHANGELOG.html from markdown source'
40
+ task :changelog => %w[ CHANGELOG.html ]
41
+ CLOBBER.include 'CHANGELOG.html'
42
+
39
43
  namespace :test_cert do
40
44
  tls_dir = Pathname('test/tls')
41
45
 
@@ -1,6 +1,20 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
+ require 'forwardable'
4
+
3
5
  module RIMS
6
+ class ServerResponseChannelError < Error
7
+ end
8
+
9
+ class ServerResponseChannelAttachError < ServerResponseChannelError
10
+ end
11
+
12
+ class ServerResponseChannelDetachError < ServerResponseChannelError
13
+ end
14
+
15
+ class ServerResponseChannelPublishError < ServerResponseChannelError
16
+ end
17
+
4
18
  class ServerResponseChannel
5
19
  def initialize
6
20
  @mutex = Thread::Mutex.new
@@ -9,14 +23,16 @@ module RIMS
9
23
 
10
24
  def make_pub_sub_pair(mbox_id)
11
25
  pub = ServerResponsePublisher.new(self, mbox_id)
12
- sub = ServerResponseSubscriber.new(self, mbox_id, pub.pub_sub_pair_key)
26
+ sub = ServerResponseSubscriber.new(self, pub)
13
27
  return pub, attach(sub)
14
28
  end
15
29
 
16
30
  def attach(sub)
17
31
  @mutex.synchronize{
18
32
  @channel[sub.mbox_id] ||= {}
19
- (@channel[sub.mbox_id].key? sub.pub_sub_pair_key) and raise ArgumentError, 'conflicted subscriber.'
33
+ if (@channel[sub.mbox_id].key? sub.pub_sub_pair_key) then
34
+ raise ServerResponseChannelAttachError.new('conflicted subscriber.', channel: self, subscriber: sub)
35
+ end
20
36
  @channel[sub.mbox_id][sub.pub_sub_pair_key] = sub
21
37
  }
22
38
 
@@ -30,8 +46,13 @@ module RIMS
30
46
  # - ServerResponseSubscriber#detach
31
47
  def detach(sub)
32
48
  @mutex.synchronize{
33
- ((@channel.key? sub.mbox_id) && (@channel[sub.mbox_id].key? sub.pub_sub_pair_key)) or raise ArgumentError, 'unregistered pub-sub pair.'
34
- (@channel[sub.mbox_id][sub.pub_sub_pair_key] == sub) or raise 'internal error: mismatched subscriber.'
49
+ unless ((@channel.key? sub.mbox_id) && (@channel[sub.mbox_id].key? sub.pub_sub_pair_key)) then
50
+ raise ServerResponseChannelDetachError.new('unregistered pub-sub pair.', channel: self, subscriber: sub)
51
+ end
52
+
53
+ unless (@channel[sub.mbox_id][sub.pub_sub_pair_key] == sub) then
54
+ raise ServerResponseChannelDetachError.new('internal error: mismatched subscriber.', channel: self, subscribe: sub)
55
+ end
35
56
 
36
57
  @channel[sub.mbox_id].delete(sub.pub_sub_pair_key)
37
58
  if (@channel[sub.mbox_id].empty?) then
@@ -74,7 +95,12 @@ module RIMS
74
95
  end
75
96
 
76
97
  def publish(response_message)
77
- @channel or raise 'detached publisher.'
98
+ unless (@channel) then
99
+ raise ServerResponseChannelPublishError.new('detached publisher.',
100
+ publisher: self,
101
+ pub_sub_pair_key: pub_sub_pair_key,
102
+ message: response_message)
103
+ end
78
104
  @channel.publish(@mbox_id, pub_sub_pair_key, response_message)
79
105
  nil
80
106
  end
@@ -89,15 +115,14 @@ module RIMS
89
115
  # do not call this method directly, call the following method
90
116
  # instead.
91
117
  # - ServerResponseChannel#make_pub_sub_pair
92
- def initialize(channel, mbox_id, pub_sub_pair_key)
118
+ def initialize(channel, pub)
93
119
  @channel = channel
94
- @mbox_id = mbox_id
95
- @pub_sub_pair_key = pub_sub_pair_key
120
+ @pub = pub
96
121
  @queue = Thread::Queue.new
97
122
  end
98
123
 
99
- attr_reader :mbox_id
100
- attr_reader :pub_sub_pair_key
124
+ extend Forwardable
125
+ def_delegators :@pub, :mbox_id, :pub_sub_pair_key
101
126
 
102
127
  # do not call this method directly, call the following method
103
128
  # instead.
@@ -325,6 +325,16 @@ module RIMS
325
325
  })
326
326
  }
327
327
  end
328
+ options.on('--daemon-umask=UMASK',
329
+ Integer,
330
+ "Umask(2). effective only with daemon command. default is `#{'%04o' % Service::DEFAULT_CONFIG.daemon_umask}'."
331
+ ) do |umask|
332
+ build.chain{|c|
333
+ c.load(daemon: {
334
+ umask: umask
335
+ })
336
+ }
337
+ end
328
338
  options.on('--status-file=FILE',
329
339
  String,
330
340
  "Name of status file. effective only with daemon command. default is `#{Service::DEFAULT_CONFIG.status_file}'."
@@ -1132,6 +1142,7 @@ module RIMS
1132
1142
  Riser::Daemon.start_daemon(daemonize: svc_conf.daemonize?,
1133
1143
  daemon_name: svc_conf.daemon_name,
1134
1144
  daemon_debug: svc_conf.daemon_debug?,
1145
+ daemon_umask: svc_conf.daemon_umask,
1135
1146
  status_file: svc_conf.status_file,
1136
1147
  listen_address: proc{
1137
1148
  # to reload on server restart
@@ -2,6 +2,13 @@
2
2
 
3
3
  module RIMS
4
4
  class Error < StandardError
5
+ def initialize(*args, **kw_args)
6
+ super(*args)
7
+ @optional_data = kw_args.dup.freeze
8
+ end
9
+
10
+ attr_reader :optional_data
11
+
5
12
  def self.trace_error_chain(exception)
6
13
  return enum_for(:trace_error_chain, exception) unless block_given?
7
14
 
@@ -12,6 +19,14 @@ module RIMS
12
19
 
13
20
  nil
14
21
  end
22
+
23
+ def self.optional_data(error) # :yields: error, data
24
+ if (error.is_a? Error) then
25
+ unless (error.optional_data.empty?) then
26
+ yield(error, error.optional_data)
27
+ end
28
+ end
29
+ end
15
30
  end
16
31
  end
17
32
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'logger'
4
4
  require 'net/imap'
5
+ require 'pp'
5
6
  require 'time'
6
7
 
7
8
  module RIMS
@@ -21,35 +22,67 @@ module RIMS
21
22
  name.upcase
22
23
  end
23
24
 
25
+ def self.logging_error_chain(error, logger)
26
+ Error.trace_error_chain(error) do |exception|
27
+ if (logger.debug?) then
28
+ Error.optional_data(exception) do |error, data|
29
+ logger.debug("error message: #{error.message} (#{error.class})")
30
+ for name, value in data
31
+ logger.debug("error data [#{name}]: #{value.pretty_inspect}")
32
+ end
33
+ end
34
+ end
35
+ logger.error(exception)
36
+ end
37
+ end
38
+
24
39
  def self.repl(decoder, limits, input, output, logger)
25
40
  input_gets = input.method(:gets)
26
- output_write = lambda{|res|
27
- last_line = nil
28
- for data in res
41
+ output_write = lambda{|data|
42
+ begin
29
43
  if (data == :flush) then
30
44
  output.flush
31
45
  else
32
46
  logger.debug("response data: #{Protocol.io_data_log(data)}") if logger.debug?
33
47
  output << data
34
- last_line = data
35
48
  end
49
+ rescue
50
+ logger.error('response write error.')
51
+ logging_error_chain($!, logger)
52
+ raise
53
+ end
54
+ }
55
+ server_output_write = lambda{|res|
56
+ for data in res
57
+ output_write.call(data)
36
58
  end
37
59
  output.flush
38
60
 
39
- last_line
61
+ nil
40
62
  }
41
- response_write = proc{|res|
42
- begin
43
- last_line = output_write.call(res)
44
- logger.info("server response: #{last_line.strip}")
45
- rescue
46
- logger.error('response write error.')
47
- logger.error($!)
48
- raise
63
+ response_write = lambda{|response|
64
+ output_write.call(response)
65
+ output.flush
66
+ logger.info("server response: #{response.strip}")
67
+ }
68
+ apply_imap_command = lambda{|name, *args, uid: false|
69
+ last_line = nil
70
+ if (uid) then
71
+ decoder.__send__(name, *args, uid: true) {|response|
72
+ output_write.call(response)
73
+ last_line = response if (response.is_a? String)
74
+ }
75
+ else
76
+ decoder.__send__(name, *args) {|response|
77
+ output_write.call(response)
78
+ last_line = response if (response.is_a? String)
79
+ }
49
80
  end
81
+ output.flush
82
+ logger.info("server response: #{last_line.strip}") if last_line
50
83
  }
51
84
 
52
- decoder.ok_greeting{|res| response_write.call(res) }
85
+ apply_imap_command.call(:ok_greeting)
53
86
 
54
87
  conn_timer = ConnectionTimer.new(limits, input.to_io)
55
88
  request_reader = RequestReader.new(input, output, logger)
@@ -61,10 +94,8 @@ module RIMS
61
94
  atom_list = request_reader.read_command
62
95
  rescue
63
96
  logger.error('invalid client command.')
64
- Error.trace_error_chain($!) do |exception|
65
- logger.error(exception)
66
- end
67
- response_write.call([ "* BAD client command syntax error\r\n" ])
97
+ logging_error_chain($!, logger)
98
+ response_write.call("* BAD client command syntax error\r\n")
68
99
  next
69
100
  end
70
101
 
@@ -100,32 +131,30 @@ module RIMS
100
131
  logger.info("uid command: #{uid_command}")
101
132
  logger.debug("uid parameter: #{uid_args}") if logger.debug?
102
133
  if (uid_name = UID_CMDs[imap_command_normalize(uid_command)]) then
103
- decoder.__send__(uid_name, tag, *uid_args, uid: true) {|res| response_write.call(res) }
134
+ apply_imap_command.call(uid_name, tag, *uid_args, uid: true)
104
135
  else
105
136
  logger.error("unknown uid command: #{uid_command}")
106
- response_write.call([ "#{tag} BAD unknown uid command\r\n" ])
137
+ response_write.call("#{tag} BAD unknown uid command\r\n")
107
138
  end
108
139
  else
109
140
  logger.error('empty uid parameter.')
110
- response_write.call([ "#{tag} BAD empty uid parameter\r\n" ])
141
+ response_write.call("#{tag} BAD empty uid parameter\r\n")
111
142
  end
112
143
  when :authenticate
113
- decoder.authenticate(tag, input_gets, output_write, *opt_args) {|res| response_write.call(res) }
144
+ apply_imap_command.call(:authenticate, tag, input_gets, server_output_write, *opt_args)
114
145
  when :idle
115
- decoder.idle(tag, input_gets, output_write, conn_timer, *opt_args) {|res| response_write.call(res) }
146
+ apply_imap_command.call(:idle, tag, input_gets, server_output_write, conn_timer, *opt_args)
116
147
  else
117
- decoder.__send__(name, tag, *opt_args) {|res| response_write.call(res) }
148
+ apply_imap_command.call(name, tag, *opt_args)
118
149
  end
119
150
  else
120
151
  logger.error("unknown command: #{command}")
121
- response_write.call([ "#{tag} BAD unknown command\r\n" ])
152
+ response_write.call("#{tag} BAD unknown command\r\n")
122
153
  end
123
154
  rescue
124
155
  logger.error('unexpected error.')
125
- Error.trace_error_chain($!) do |exception|
126
- logger.error(exception)
127
- end
128
- response_write.call([ "#{tag} BAD unexpected error\r\n" ])
156
+ logging_error_chain($!, logger)
157
+ response_write.call("#{tag} BAD unexpected error\r\n")
129
158
  end
130
159
 
131
160
  if (normalized_command == 'LOGOUT') then
@@ -137,9 +166,9 @@ module RIMS
137
166
 
138
167
  if (conn_timer.command_wait_timeout?) then
139
168
  if (limits.command_wait_timeout_seconds > 0) then
140
- response_write.call([ "* BYE server autologout: idle for too long\r\n" ])
169
+ response_write.call("* BYE server autologout: idle for too long\r\n")
141
170
  else
142
- response_write.call([ "* BYE server autologout: shutdown\r\n" ])
171
+ response_write.call("* BYE server autologout: shutdown\r\n")
143
172
  end
144
173
  end
145
174
 
@@ -157,25 +186,10 @@ module RIMS
157
186
 
158
187
  attr_reader :next_decoder
159
188
 
160
- def response_stream(tag)
161
- Enumerator.new{|res|
162
- begin
163
- yield(res)
164
- rescue SyntaxError
165
- @logger.error('client command syntax error.')
166
- @logger.error($!)
167
- res << "#{tag} BAD client command syntax error\r\n"
168
- rescue
169
- raise if ($!.class.name =~ /AssertionFailedError/)
170
- @logger.error('internal server error.')
171
- Error.trace_error_chain($!) do |exception|
172
- @logger.error(exception)
173
- end
174
- res << "#{tag} BAD internal server error\r\n"
175
- end
176
- }
189
+ def logging_error_chain(error)
190
+ self.class.logging_error_chain(error, @logger)
177
191
  end
178
- private :response_stream
192
+ private :logging_error_chain
179
193
 
180
194
  def guard_error(imap_command, tag, *args, **kw_args, &block)
181
195
  begin
@@ -186,19 +200,17 @@ module RIMS
186
200
  end
187
201
  rescue SyntaxError
188
202
  @logger.error('client command syntax error.')
189
- @logger.error($!)
190
- yield([ "#{tag} BAD client command syntax error\r\n" ])
203
+ logging_error_chain($!)
204
+ yield("#{tag} BAD client command syntax error\r\n")
191
205
  rescue ArgumentError
192
206
  @logger.error('invalid command parameter.')
193
- @logger.error($!)
194
- yield([ "#{tag} BAD invalid command parameter\r\n" ])
207
+ logging_error_chain($!)
208
+ yield("#{tag} BAD invalid command parameter\r\n")
195
209
  rescue
196
210
  raise if ($!.class.name =~ /AssertionFailedError/)
197
211
  @logger.error('internal server error.')
198
- Error.trace_error_chain($!) do |exception|
199
- @logger.error(exception)
200
- end
201
- yield([ "#{tag} BAD internal server error\r\n" ])
212
+ logging_error_chain($!)
213
+ yield("#{tag} BAD internal server error\r\n")
202
214
  end
203
215
  end
204
216
  private :guard_error
@@ -256,8 +268,7 @@ module RIMS
256
268
  end
257
269
  private :imap_command
258
270
 
259
- def make_engine_and_recovery_if_needed(drb_services, username,
260
- logger: Logger.new(STDOUT))
271
+ def make_engine_and_recovery_if_needed(drb_services, username, logger: Logger.new(STDOUT))
261
272
  unique_user_id = Authentication.unique_user_id(username)
262
273
  logger.debug("unique user ID: #{username} -> #{unique_user_id}") if logger.debug?
263
274
 
@@ -265,7 +276,7 @@ module RIMS
265
276
  engine = drb_services[:engine, unique_user_id]
266
277
 
267
278
  begin
268
- engine.recovery_if_needed(username) {|msg| yield(msg) }
279
+ engine.recovery_if_needed(username) {|response| yield(response) }
269
280
  rescue
270
281
  engine.destroy
271
282
  raise
@@ -276,14 +287,13 @@ module RIMS
276
287
  end
277
288
 
278
289
  def make_logout_response(tag)
279
- [ "* BYE server logout\r\n",
280
- "#{tag} OK LOGOUT completed\r\n"
281
- ]
290
+ yield("* BYE server logout\r\n")
291
+ yield("#{tag} OK LOGOUT completed\r\n")
282
292
  end
283
293
  private :make_logout_response
284
294
 
285
295
  def ok_greeting
286
- yield([ "* OK RIMS v#{VERSION} IMAP4rev1 service ready.\r\n" ])
296
+ yield("* OK RIMS v#{VERSION} IMAP4rev1 service ready.\r\n")
287
297
  end
288
298
 
289
299
  # common IMAP command
@@ -292,10 +302,8 @@ module RIMS
292
302
  def capability(tag)
293
303
  capability_list = %w[ IMAP4rev1 UIDPLUS IDLE ]
294
304
  capability_list += @auth.capability.map{|auth_capability| "AUTH=#{auth_capability}" }
295
- res = []
296
- res << "* CAPABILITY #{capability_list.join(' ')}\r\n"
297
- res << "#{tag} OK CAPABILITY completed\r\n"
298
- yield(res)
305
+ yield("* CAPABILITY #{capability_list.join(' ')}\r\n")
306
+ yield("#{tag} OK CAPABILITY completed\r\n")
299
307
  end
300
308
  imap_command :capability
301
309
  end
@@ -330,6 +338,7 @@ module RIMS
330
338
  super(auth, logger)
331
339
  @drb_services = drb_services
332
340
  @mail_delivery_user = mail_delivery_user
341
+ @logger.debug("RIMS::Protocol::InitialDecoder#initialize at #{self}") if @logger.debug?
333
342
  end
334
343
 
335
344
  def auth?
@@ -341,22 +350,23 @@ module RIMS
341
350
  end
342
351
 
343
352
  def cleanup
353
+ @logger.debug("RIMS::Protocol::InitialDecoder#cleanup at #{self}") if @logger.debug?
344
354
  nil
345
355
  end
346
356
 
347
357
  def make_not_authenticated_response(tag)
348
- [ "#{tag} NO not authenticated\r\n" ]
358
+ yield("#{tag} NO not authenticated\r\n")
349
359
  end
350
360
  private :make_not_authenticated_response
351
361
 
352
362
  def noop(tag)
353
- yield([ "#{tag} OK NOOP completed\r\n" ])
363
+ yield("#{tag} OK NOOP completed\r\n")
354
364
  end
355
365
  imap_command :noop
356
366
 
357
- def logout(tag)
358
- @next_decoder = LogoutDecoder.new(self)
359
- yield(make_logout_response(tag))
367
+ def logout(tag, &block)
368
+ @next_decoder = LogoutDecoder.new(self, @logger)
369
+ make_logout_response(tag, &block)
360
370
  end
361
371
  imap_command :logout
362
372
 
@@ -366,7 +376,7 @@ module RIMS
366
376
  @logger.info("mail delivery user: #{username}")
367
377
  MailDeliveryDecoder.new(self, @drb_services, @auth, @logger)
368
378
  else
369
- engine = self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|msg| yield(msg) }
379
+ engine = self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|untagged_response| yield(untagged_response) }
370
380
  UserMailboxDecoder.new(self, engine, @auth, @logger)
371
381
  end
372
382
  end
@@ -377,137 +387,137 @@ module RIMS
377
387
  auth_reader = AuthenticationReader.new(@auth, client_response_input_gets, server_challenge_output_write, @logger)
378
388
  if (username = auth_reader.authenticate_client(auth_type, inline_client_response_data_base64)) then
379
389
  if (username != :*) then
380
- yield response_stream(tag) {|res|
381
- @logger.info("authentication OK: #{username}")
382
- @next_decoder = accept_authentication(username) {|msg| res << msg << :flush }
383
- res << "#{tag} OK AUTHENTICATE #{auth_type} success\r\n"
390
+ @logger.info("authentication OK: #{username}")
391
+ @next_decoder = accept_authentication(username) {|untagged_response|
392
+ yield(untagged_response)
393
+ yield(:flush)
384
394
  }
395
+ yield("#{tag} OK AUTHENTICATE #{auth_type} success\r\n")
385
396
  else
386
397
  @logger.info('bad authentication.')
387
- yield([ "#{tag} BAD AUTHENTICATE failed\r\n" ])
398
+ yield("#{tag} BAD AUTHENTICATE failed\r\n")
388
399
  end
389
400
  else
390
- yield([ "#{tag} NO authentication failed\r\n" ])
401
+ yield("#{tag} NO authentication failed\r\n")
391
402
  end
392
403
  end
393
404
  imap_command :authenticate
394
405
 
395
406
  def login(tag, username, password)
396
407
  if (@auth.authenticate_login(username, password)) then
397
- yield response_stream(tag) {|res|
398
- @logger.info("login authentication OK: #{username}")
399
- @next_decoder = accept_authentication(username) {|msg| res << msg << :flush }
400
- res << "#{tag} OK LOGIN completed\r\n"
408
+ @logger.info("login authentication OK: #{username}")
409
+ @next_decoder = accept_authentication(username) {|untagged_response|
410
+ yield(untagged_response)
411
+ yield(:flush)
401
412
  }
413
+ yield("#{tag} OK LOGIN completed\r\n")
402
414
  else
403
- yield([ "#{tag} NO failed to login\r\n" ])
415
+ yield("#{tag} NO failed to login\r\n")
404
416
  end
405
417
  end
406
418
  imap_command :login
407
419
 
408
- def select(tag, mbox_name)
409
- yield(make_not_authenticated_response(tag))
420
+ def select(tag, mbox_name, &block)
421
+ make_not_authenticated_response(tag, &block)
410
422
  end
411
423
  imap_command :select
412
424
 
413
- def examine(tag, mbox_name)
414
- yield(make_not_authenticated_response(tag))
425
+ def examine(tag, mbox_name, &block)
426
+ make_not_authenticated_response(tag, &block)
415
427
  end
416
428
  imap_command :examine
417
429
 
418
- def create(tag, mbox_name)
419
- yield(make_not_authenticated_response(tag))
430
+ def create(tag, mbox_name, &block)
431
+ make_not_authenticated_response(tag, &block)
420
432
  end
421
433
  imap_command :create
422
434
 
423
- def delete(tag, mbox_name)
424
- yield(make_not_authenticated_response(tag))
435
+ def delete(tag, mbox_name, &block)
436
+ make_not_authenticated_response(tag, &block)
425
437
  end
426
438
  imap_command :delete
427
439
 
428
- def rename(tag, src_name, dst_name)
429
- yield(make_not_authenticated_response(tag))
440
+ def rename(tag, src_name, dst_name, &block)
441
+ make_not_authenticated_response(tag, &block)
430
442
  end
431
443
  imap_command :rename
432
444
 
433
- def subscribe(tag, mbox_name)
434
- yield(make_not_authenticated_response(tag))
445
+ def subscribe(tag, mbox_name, &block)
446
+ make_not_authenticated_response(tag, &block)
435
447
  end
436
448
  imap_command :subscribe
437
449
 
438
- def unsubscribe(tag, mbox_name)
439
- yield(make_not_authenticated_response(tag))
450
+ def unsubscribe(tag, mbox_name, &block)
451
+ make_not_authenticated_response(tag, &block)
440
452
  end
441
453
  imap_command :unsubscribe
442
454
 
443
- def list(tag, ref_name, mbox_name)
444
- yield(make_not_authenticated_response(tag))
455
+ def list(tag, ref_name, mbox_name, &block)
456
+ make_not_authenticated_response(tag, &block)
445
457
  end
446
458
  imap_command :list
447
459
 
448
- def lsub(tag, ref_name, mbox_name)
449
- yield(make_not_authenticated_response(tag))
460
+ def lsub(tag, ref_name, mbox_name, &block)
461
+ make_not_authenticated_response(tag, &block)
450
462
  end
451
463
  imap_command :lsub
452
464
 
453
- def status(tag, mbox_name, data_item_group)
454
- yield(make_not_authenticated_response(tag))
465
+ def status(tag, mbox_name, data_item_group, &block)
466
+ make_not_authenticated_response(tag, &block)
455
467
  end
456
468
  imap_command :status
457
469
 
458
- def append(tag, mbox_name, *opt_args, msg_text)
459
- yield(make_not_authenticated_response(tag))
470
+ def append(tag, mbox_name, *opt_args, msg_text, &block)
471
+ make_not_authenticated_response(tag, &block)
460
472
  end
461
473
  imap_command :append
462
474
 
463
- def check(tag)
464
- yield(make_not_authenticated_response(tag))
475
+ def check(tag, &block)
476
+ make_not_authenticated_response(tag, &block)
465
477
  end
466
478
  imap_command :check
467
479
 
468
- def close(tag)
469
- yield(make_not_authenticated_response(tag))
480
+ def close(tag, &block)
481
+ make_not_authenticated_response(tag, &block)
470
482
  end
471
483
  imap_command :close
472
484
 
473
- def expunge(tag)
474
- yield(make_not_authenticated_response(tag))
485
+ def expunge(tag, &block)
486
+ make_not_authenticated_response(tag, &block)
475
487
  end
476
488
  imap_command :expunge
477
489
 
478
- def search(tag, *cond_args, uid: false)
479
- yield(make_not_authenticated_response(tag))
490
+ def search(tag, *cond_args, uid: false, &block)
491
+ make_not_authenticated_response(tag, &block)
480
492
  end
481
493
  imap_command :search
482
494
 
483
- def fetch(tag, msg_set, data_item_group, uid: false)
484
- yield(make_not_authenticated_response(tag))
495
+ def fetch(tag, msg_set, data_item_group, uid: false, &block)
496
+ make_not_authenticated_response(tag, &block)
485
497
  end
486
498
  imap_command :fetch
487
499
 
488
- def store(tag, msg_set, data_item_name, data_item_value, uid: false)
489
- yield(make_not_authenticated_response(tag))
500
+ def store(tag, msg_set, data_item_name, data_item_value, uid: false, &block)
501
+ make_not_authenticated_response(tag, &block)
490
502
  end
491
503
  imap_command :store
492
504
 
493
- def copy(tag, msg_set, mbox_name, uid: false)
494
- yield(make_not_authenticated_response(tag))
505
+ def copy(tag, msg_set, mbox_name, uid: false, &block)
506
+ make_not_authenticated_response(tag, &block)
495
507
  end
496
508
  imap_command :copy
497
509
 
498
- def idle(tag, client_input_gets, server_output_write, connection_timer)
499
- yield(make_not_authenticated_response(tag))
510
+ def idle(tag, client_input_gets, server_output_write, connection_timer, &block)
511
+ make_not_authenticated_response(tag, &block)
500
512
  end
501
513
  imap_command :idle
502
514
  end
503
515
 
504
516
  class LogoutDecoder < Decoder
505
- def initialize(parent_decoder)
517
+ def initialize(parent_decoder, logger)
518
+ super(nil, logger)
506
519
  @parent_decoder = parent_decoder
507
- end
508
-
509
- def next_decoder
510
- self
520
+ @logger.debug("RIMS::Protocol::LogoutDecoder#initialize at #{self}") if @logger.debug?
511
521
  end
512
522
 
513
523
  def auth?
@@ -519,6 +529,7 @@ module RIMS
519
529
  end
520
530
 
521
531
  def cleanup
532
+ @logger.debug("RIMS::Protocol::LogoutDecoder#cleanup at #{self}") if @logger.debug?
522
533
  unless (@parent_decoder.nil?) then
523
534
  @parent_decoder.cleanup
524
535
  @parent_decoder = nil
@@ -652,20 +663,64 @@ module RIMS
652
663
  class AuthenticatedDecoder < Decoder
653
664
  def authenticate(tag, client_response_input_gets, server_challenge_output_write,
654
665
  auth_type, inline_client_response_data_base64=nil, &block)
655
- yield([ "#{tag} NO duplicated authentication\r\n" ])
666
+ yield("#{tag} NO duplicated authentication\r\n")
656
667
  end
657
668
  imap_command :authenticate
658
669
 
659
670
  def login(tag, username, password, &block)
660
- yield([ "#{tag} NO duplicated login\r\n" ])
671
+ yield("#{tag} NO duplicated login\r\n")
661
672
  end
662
673
  imap_command :login
663
674
  end
664
675
 
665
676
  class UserMailboxDecoder < AuthenticatedDecoder
677
+ class BulkResponse
678
+ def initialize(limit_count, limit_size)
679
+ @limit_count = limit_count
680
+ @limit_size = limit_size
681
+ @responses = []
682
+ @size = 0
683
+ end
684
+
685
+ def count
686
+ @responses.length
687
+ end
688
+
689
+ attr_reader :size
690
+
691
+ def add(response)
692
+ @responses << response
693
+ @size += response.bytesize
694
+ self
695
+ end
696
+
697
+ alias << add
698
+
699
+ def empty?
700
+ @responses.empty?
701
+ end
702
+
703
+ def full?
704
+ count >= @limit_count || size >= @limit_size
705
+ end
706
+
707
+ def flush
708
+ res = @responses
709
+ if (count >= @limit_count) then
710
+ res = [ res.join('') ]
711
+ end
712
+
713
+ @responses = []
714
+ @size = 0
715
+
716
+ res
717
+ end
718
+ end
719
+
666
720
  class Engine
667
721
  def initialize(unique_user_id, mail_store, logger,
668
722
  bulk_response_count: 100,
723
+ bulk_response_size: 1024**2 * 10,
669
724
  read_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
670
725
  write_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
671
726
  cleanup_write_lock_timeout_seconds: 1,
@@ -675,12 +730,14 @@ module RIMS
675
730
  @mail_store = mail_store
676
731
  @logger = logger
677
732
  @bulk_response_count = bulk_response_count
733
+ @bulk_response_size = bulk_response_size
678
734
  @read_lock_timeout_seconds = read_lock_timeout_seconds
679
735
  @write_lock_timeout_seconds = write_lock_timeout_seconds
680
736
  @cleanup_write_lock_timeout_seconds = cleanup_write_lock_timeout_seconds
681
737
  @charset_aliases = charset_aliases
682
738
  @charset_convert_options = charset_convert_options
683
739
  @folders = {}
740
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#initialize at #{self}") if @logger.debug?
684
741
  end
685
742
 
686
743
  attr_reader :unique_user_id
@@ -708,11 +765,13 @@ module RIMS
708
765
  end
709
766
  @folders[token] = folder
710
767
 
768
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#open_folder: #{token}") if @logger.debug?
711
769
  token
712
770
  end
713
771
  private :open_folder
714
772
 
715
773
  def close_folder(token)
774
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#close_folder: #{token}") if @logger.debug?
716
775
  folder = @folders.delete(token) or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
717
776
  folder.reload if folder.updated?
718
777
  begin
@@ -756,9 +815,10 @@ module RIMS
756
815
  end
757
816
 
758
817
  def destroy
818
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#destroy at #{self}") if @logger.debug?
759
819
  tmp_mail_store = @mail_store
760
820
  ReadWriteLock.write_lock_timeout_detach(@cleanup_write_lock_timeout_seconds, @write_lock_timeout_seconds, logger: @logger) {|timeout_seconds|
761
- @mail_store.write_synchronize(timeout_seconds) {
821
+ tmp_mail_store.write_synchronize(timeout_seconds) {
762
822
  @logger.info("close mail store: #{@unique_user_id}")
763
823
  tmp_mail_store.close
764
824
  }
@@ -828,20 +888,28 @@ module RIMS
828
888
  private :imap_command_selected
829
889
  end
830
890
 
891
+ def new_bulk_response
892
+ BulkResponse.new(@bulk_response_count, @bulk_response_size)
893
+ end
894
+ private :new_bulk_response
895
+
831
896
  def noop(token, tag)
832
- res = []
897
+ res = new_bulk_response
833
898
  if (token) then
834
899
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
835
900
  begin
836
901
  @mail_store.read_synchronize(@read_lock_timeout_seconds) {
837
- folder.server_response_fetch{|r| res << r } if folder.server_response?
902
+ folder.server_response_fetch{|untagged_response|
903
+ res << untagged_response
904
+ yield(res.flush) if res.full?
905
+ }
838
906
  }
839
907
  rescue ReadLockTimeoutError
840
908
  @logger.warn("give up to get folder status because of read-lock timeout over #{@read_lock_timeout_seconds} seconds")
841
909
  end
842
910
  end
843
911
  res << "#{tag} OK NOOP completed\r\n"
844
- yield(res)
912
+ yield(res.flush)
845
913
  end
846
914
 
847
915
  def folder_open_msgs(token)
@@ -863,20 +931,21 @@ module RIMS
863
931
  close_no_response(token)
864
932
  end
865
933
 
866
- res = []
934
+ res = new_bulk_response
867
935
  new_token = nil
868
936
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
869
937
 
870
938
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
871
939
  new_token = open_folder(id)
872
- folder_open_msgs(new_token) do |msg|
873
- res << msg
874
- end
940
+ folder_open_msgs(new_token) {|untagged_response|
941
+ res << untagged_response
942
+ yield(res.flush) if res.full?
943
+ }
875
944
  res << "#{tag} OK [READ-WRITE] SELECT completed\r\n"
876
945
  else
877
946
  res << "#{tag} NO not found a mailbox\r\n"
878
947
  end
879
- yield(res)
948
+ yield(res.flush)
880
949
 
881
950
  new_token
882
951
  end
@@ -887,30 +956,34 @@ module RIMS
887
956
  close_no_response(token)
888
957
  end
889
958
 
890
- res = []
959
+ res = new_bulk_response
891
960
  new_token = nil
892
961
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
893
962
 
894
963
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
895
964
  new_token = open_folder(id, read_only: true)
896
- folder_open_msgs(new_token) do |msg|
897
- res << msg
898
- end
965
+ folder_open_msgs(new_token) {|untagged_response|
966
+ res << untagged_response
967
+ yield(res.flush) if res.full?
968
+ }
899
969
  res << "#{tag} OK [READ-ONLY] EXAMINE completed\r\n"
900
970
  else
901
971
  res << "#{tag} NO not found a mailbox\r\n"
902
972
  end
903
- yield(res)
973
+ yield(res.flush)
904
974
 
905
975
  new_token
906
976
  end
907
977
  imap_command_authenticated :examine
908
978
 
909
979
  def create(token, tag, mbox_name)
910
- res = []
980
+ res = new_bulk_response
911
981
  if (token) then
912
982
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
913
- folder.server_response_fetch{|r| res << r }
983
+ folder.server_response_fetch{|untagged_response|
984
+ res << untagged_response
985
+ yield(res.flush) if res.full?
986
+ }
914
987
  end
915
988
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
916
989
  if (@mail_store.mbox_id(mbox_name_utf8)) then
@@ -919,15 +992,18 @@ module RIMS
919
992
  @mail_store.add_mbox(mbox_name_utf8)
920
993
  res << "#{tag} OK CREATE completed\r\n"
921
994
  end
922
- yield(res)
995
+ yield(res.flush)
923
996
  end
924
997
  imap_command_authenticated :create, exclusive: true
925
998
 
926
999
  def delete(token, tag, mbox_name)
927
- res = []
1000
+ res = new_bulk_response
928
1001
  if (token) then
929
1002
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
930
- folder.server_response_fetch{|r| res << r }
1003
+ folder.server_response_fetch{|untagged_response|
1004
+ res << untagged_response
1005
+ yield(res.flush) if res.full?
1006
+ }
931
1007
  end
932
1008
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
933
1009
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
@@ -940,38 +1016,47 @@ module RIMS
940
1016
  else
941
1017
  res << "#{tag} NO not found a mailbox\r\n"
942
1018
  end
943
- yield(res)
1019
+ yield(res.flush)
944
1020
  end
945
1021
  imap_command_authenticated :delete, exclusive: true
946
1022
 
947
1023
  def rename(token, tag, src_name, dst_name)
948
- res = []
1024
+ res = new_bulk_response
949
1025
  if (token) then
950
1026
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
951
- folder.server_response_fetch{|r| res << r }
1027
+ folder.server_response_fetch{|untagged_response|
1028
+ res << untagged_response
1029
+ yield(res.flush) if res.full?
1030
+ }
952
1031
  end
953
1032
  src_name_utf8 = Net::IMAP.decode_utf7(src_name)
954
1033
  dst_name_utf8 = Net::IMAP.decode_utf7(dst_name)
955
1034
  unless (id = @mail_store.mbox_id(src_name_utf8)) then
956
- return yield(res << "#{tag} NO not found a mailbox\r\n")
1035
+ res << "#{tag} NO not found a mailbox\r\n"
1036
+ return yield(res.flush)
957
1037
  end
958
1038
  if (id == @mail_store.mbox_id('INBOX')) then
959
- return yield(res << "#{tag} NO not rename inbox\r\n")
1039
+ res << "#{tag} NO not rename inbox\r\n"
1040
+ return yield(res.flush)
960
1041
  end
961
1042
  if (@mail_store.mbox_id(dst_name_utf8)) then
962
- return yield(res << "#{tag} NO duplicated mailbox\r\n")
1043
+ res << "#{tag} NO duplicated mailbox\r\n"
1044
+ return yield(res.flush)
963
1045
  end
964
1046
  @mail_store.rename_mbox(id, dst_name_utf8)
965
1047
  res << "#{tag} OK RENAME completed\r\n"
966
- yield(res)
1048
+ yield(res.flush)
967
1049
  end
968
1050
  imap_command_authenticated :rename, exclusive: true
969
1051
 
970
1052
  def subscribe(token, tag, mbox_name)
971
- res = []
1053
+ res = new_bulk_response
972
1054
  if (token) then
973
1055
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
974
- folder.server_response_fetch{|r| res << r }
1056
+ folder.server_response_fetch{|untagged_response|
1057
+ res << untagged_response
1058
+ yield(res.flush) if res.full?
1059
+ }
975
1060
  end
976
1061
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
977
1062
  if (@mail_store.mbox_id(mbox_name_utf8)) then
@@ -979,15 +1064,18 @@ module RIMS
979
1064
  else
980
1065
  res << "#{tag} NO not found a mailbox\r\n"
981
1066
  end
982
- yield(res)
1067
+ yield(res.flush)
983
1068
  end
984
1069
  imap_command_authenticated :subscribe
985
1070
 
986
1071
  def unsubscribe(token, tag, mbox_name)
987
- res = []
1072
+ res = new_bulk_response
988
1073
  if (token) then
989
1074
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
990
- folder.server_response_fetch{|r| res << r }
1075
+ folder.server_response_fetch{|untagged_response|
1076
+ res << untagged_response
1077
+ yield(res.flush) if res.full?
1078
+ }
991
1079
  end
992
1080
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
993
1081
  if (@mail_store.mbox_id(mbox_name_utf8)) then
@@ -995,7 +1083,7 @@ module RIMS
995
1083
  else
996
1084
  res << "#{tag} NO not found a mailbox\r\n"
997
1085
  end
998
- yield(res)
1086
+ yield(res.flush)
999
1087
  end
1000
1088
  imap_command_authenticated :unsubscribe
1001
1089
 
@@ -1024,46 +1112,57 @@ module RIMS
1024
1112
  private :list_mbox
1025
1113
 
1026
1114
  def list(token, tag, ref_name, mbox_name)
1027
- res = []
1115
+ res = new_bulk_response
1028
1116
  if (token) then
1029
1117
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1030
- folder.server_response_fetch{|r| res << r }
1118
+ folder.server_response_fetch{|untagged_response|
1119
+ res << untagged_response
1120
+ yield(res.flush) if res.full?
1121
+ }
1031
1122
  end
1032
1123
  if (mbox_name.empty?) then
1033
1124
  res << "* LIST (\\Noselect) NIL \"\"\r\n"
1034
1125
  else
1035
- list_mbox(ref_name, mbox_name) do |mbox_entry|
1126
+ list_mbox(ref_name, mbox_name) {|mbox_entry|
1036
1127
  res << "* LIST #{mbox_entry}\r\n"
1037
- end
1128
+ yield(res.flush) if res.full?
1129
+ }
1038
1130
  end
1039
1131
  res << "#{tag} OK LIST completed\r\n"
1040
- yield(res)
1132
+ yield(res.flush)
1041
1133
  end
1042
1134
  imap_command_authenticated :list
1043
1135
 
1044
1136
  def lsub(token, tag, ref_name, mbox_name)
1045
- res = []
1137
+ res = new_bulk_response
1046
1138
  if (token) then
1047
1139
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1048
- folder.server_response_fetch{|r| res << r }
1140
+ folder.server_response_fetch{|untagged_response|
1141
+ res << untagged_response
1142
+ yield(res.flush) if res.full?
1143
+ }
1049
1144
  end
1050
1145
  if (mbox_name.empty?) then
1051
1146
  res << "* LSUB (\\Noselect) NIL \"\"\r\n"
1052
1147
  else
1053
- list_mbox(ref_name, mbox_name) do |mbox_entry|
1148
+ list_mbox(ref_name, mbox_name) {|mbox_entry|
1054
1149
  res << "* LSUB #{mbox_entry}\r\n"
1055
- end
1150
+ yield(res.flush) if res.full?
1151
+ }
1056
1152
  end
1057
1153
  res << "#{tag} OK LSUB completed\r\n"
1058
- yield(res)
1154
+ yield(res.flush)
1059
1155
  end
1060
1156
  imap_command_authenticated :lsub
1061
1157
 
1062
1158
  def status(token, tag, mbox_name, data_item_group)
1063
- res = []
1159
+ res = new_bulk_response
1064
1160
  if (token) then
1065
1161
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1066
- folder.server_response_fetch{|r| res << r }
1162
+ folder.server_response_fetch{|untagged_response|
1163
+ res << untagged_response
1164
+ yield(res.flush) if res.full?
1165
+ }
1067
1166
  end
1068
1167
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
1069
1168
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
@@ -1095,7 +1194,7 @@ module RIMS
1095
1194
  else
1096
1195
  res << "#{tag} NO not found a mailbox\r\n"
1097
1196
  end
1098
- yield(res)
1197
+ yield(res.flush)
1099
1198
  end
1100
1199
  imap_command_authenticated :status
1101
1200
 
@@ -1116,7 +1215,7 @@ module RIMS
1116
1215
  private :mailbox_size_server_response_multicast_push
1117
1216
 
1118
1217
  def append(token, tag, mbox_name, *opt_args, msg_text)
1119
- res = []
1218
+ res = new_bulk_response
1120
1219
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
1121
1220
  if (mbox_id = @mail_store.mbox_id(mbox_name_utf8)) then
1122
1221
  msg_flags = []
@@ -1165,30 +1264,39 @@ module RIMS
1165
1264
  mailbox_size_server_response_multicast_push(mbox_id)
1166
1265
  if (token) then
1167
1266
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1168
- folder.server_response_fetch{|r| res << r }
1267
+ folder.server_response_fetch{|untagged_response|
1268
+ res << untagged_response
1269
+ yield(res.flush) if res.full?
1270
+ }
1169
1271
  end
1170
1272
 
1171
1273
  res << "#{tag} OK [APPENDUID #{mbox_id} #{uid}] APPEND completed\r\n"
1172
1274
  else
1173
1275
  if (token) then
1174
1276
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1175
- folder.server_response_fetch{|r| res << r }
1277
+ folder.server_response_fetch{|untagged_response|
1278
+ res << untagged_response
1279
+ yield(res.flush) if res.full?
1280
+ }
1176
1281
  end
1177
1282
  res << "#{tag} NO [TRYCREATE] not found a mailbox\r\n"
1178
1283
  end
1179
- yield(res)
1284
+ yield(res.flush)
1180
1285
  end
1181
1286
  imap_command_authenticated :append, exclusive: true
1182
1287
 
1183
1288
  def check(token, tag)
1184
- res = []
1289
+ res = new_bulk_response
1185
1290
  if (token) then
1186
1291
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1187
- folder.server_response_fetch{|r| res << r }
1292
+ folder.server_response_fetch{|untagged_response|
1293
+ res << untagged_response
1294
+ yield(res.flush) if res.full?
1295
+ }
1188
1296
  end
1189
1297
  @mail_store.sync
1190
1298
  res << "#{tag} OK CHECK completed\r\n"
1191
- yield(res)
1299
+ yield(res.flush)
1192
1300
  end
1193
1301
  imap_command_selected :check, exclusive: true
1194
1302
 
@@ -1216,27 +1324,21 @@ module RIMS
1216
1324
  return yield([ "#{tag} NO cannot expunge in read-only mode\r\n" ]) if folder.read_only?
1217
1325
  folder.reload if folder.updated?
1218
1326
 
1219
- res = []
1220
- folder.server_response_fetch{|r|
1221
- res << r
1222
- if (res.length >= @bulk_response_count) then
1223
- yield(res)
1224
- res = []
1225
- end
1226
- }
1327
+ res = new_bulk_response
1328
+ folder.server_response_fetch{|untagged_response|
1329
+ res << untagged_response
1330
+ yield(res.flush) if res.full?
1331
+ }
1227
1332
 
1228
1333
  folder.expunge_mbox do |msg_num|
1229
- r = "* #{msg_num} EXPUNGE\r\n"
1230
- res << r
1231
- if (res.length >= @bulk_response_count) then
1232
- yield(res)
1233
- res = []
1234
- end
1235
- folder.server_response_multicast_push(r)
1334
+ untagged_response = "* #{msg_num} EXPUNGE\r\n"
1335
+ res << untagged_response
1336
+ yield(res.flush) if res.full?
1337
+ folder.server_response_multicast_push(untagged_response)
1236
1338
  end
1237
1339
 
1238
1340
  res << "#{tag} OK EXPUNGE completed\r\n"
1239
- yield(res)
1341
+ yield(res.flush)
1240
1342
  end
1241
1343
  imap_command_selected :expunge, exclusive: true
1242
1344
 
@@ -1283,13 +1385,10 @@ module RIMS
1283
1385
  end
1284
1386
  cond = parser.parse(cond_args)
1285
1387
 
1286
- res = []
1287
- folder.server_response_fetch{|r|
1288
- res << r
1289
- if (res.length >= @bulk_response_count) then
1290
- yield(res)
1291
- res = []
1292
- end
1388
+ res = new_bulk_response
1389
+ folder.server_response_fetch{|untagged_response|
1390
+ res << untagged_response
1391
+ yield(res.flush) if res.full?
1293
1392
  }
1294
1393
 
1295
1394
  res << '* SEARCH'
@@ -1303,10 +1402,7 @@ module RIMS
1303
1402
  else
1304
1403
  res << " #{msg.num}"
1305
1404
  end
1306
- if (res.length >= @bulk_response_count) then
1307
- yield(res)
1308
- res = []
1309
- end
1405
+ yield(res.flush) if res.full?
1310
1406
  end
1311
1407
  rescue EncodingError
1312
1408
  @logger.warn("encoding error at the message: uidvalidity(#{folder.mbox_id}) uid(#{msg.uid})")
@@ -1317,14 +1413,12 @@ module RIMS
1317
1413
  res << "\r\n"
1318
1414
  end
1319
1415
  rescue
1320
- # flush bulk response
1321
- yield(res)
1322
- res = []
1416
+ yield(res.flush)
1323
1417
  raise
1324
1418
  end
1325
1419
 
1326
1420
  res << "#{tag} OK SEARCH completed\r\n"
1327
- yield(res)
1421
+ yield(res.flush)
1328
1422
  end
1329
1423
  imap_command_selected :search
1330
1424
 
@@ -1348,25 +1442,19 @@ module RIMS
1348
1442
  parser = FetchParser.new(@mail_store, folder, charset_aliases: @charset_aliases)
1349
1443
  fetch = parser.parse(data_item_group)
1350
1444
 
1351
- res = []
1352
- folder.server_response_fetch{|r|
1353
- res << r
1354
- if (res.length >= @bulk_response_count) then
1355
- yield(res)
1356
- res = []
1357
- end
1445
+ res = new_bulk_response
1446
+ folder.server_response_fetch{|untagged_response|
1447
+ res << untagged_response
1448
+ yield(res.flush) if res.full?
1358
1449
  }
1359
1450
 
1360
1451
  for msg in msg_list
1361
1452
  res << ('* '.b << msg.num.to_s.b << ' FETCH '.b << fetch.call(msg) << "\r\n".b)
1362
- if (res.length >= @bulk_response_count) then
1363
- yield(res)
1364
- res = []
1365
- end
1453
+ yield(res.flush) if res.full?
1366
1454
  end
1367
1455
 
1368
1456
  res << "#{tag} OK FETCH completed\r\n"
1369
- yield(res)
1457
+ yield(res.flush)
1370
1458
  end
1371
1459
  imap_command_selected :fetch
1372
1460
 
@@ -1446,18 +1534,15 @@ module RIMS
1446
1534
  end
1447
1535
  end
1448
1536
 
1449
- res = []
1450
- folder.server_response_fetch{|r|
1451
- res << r
1452
- if (res.length >= @bulk_response_count) then
1453
- yield(res)
1454
- res = []
1455
- end
1537
+ res = new_bulk_response
1538
+ folder.server_response_fetch{|untagged_response|
1539
+ res << untagged_response
1540
+ yield(res.flush) if res.full?
1456
1541
  }
1457
1542
 
1458
1543
  if (is_silent) then
1459
1544
  res << "#{tag} OK STORE completed\r\n"
1460
- yield(res)
1545
+ yield(res.flush)
1461
1546
  else
1462
1547
  for msg in msg_list
1463
1548
  flag_atom_list = nil
@@ -1477,17 +1562,14 @@ module RIMS
1477
1562
  else
1478
1563
  res << "* #{msg.num} FETCH (FLAGS (#{flag_atom_list.join(' ')}))\r\n"
1479
1564
  end
1480
- if (res.length >= @bulk_response_count) then
1481
- yield(res)
1482
- res = []
1483
- end
1565
+ yield(res.flush) if res.full?
1484
1566
  else
1485
1567
  @logger.warn("not found a message and skipped: uidvalidity(#{folder.mbox_id}) uid(#{msg.uid})")
1486
1568
  end
1487
1569
  end
1488
1570
 
1489
1571
  res << "#{tag} OK STORE completed\r\n"
1490
- yield(res)
1572
+ yield(res.flush)
1491
1573
  end
1492
1574
  end
1493
1575
  imap_command_selected :store, exclusive: true
@@ -1497,7 +1579,7 @@ module RIMS
1497
1579
  folder.should_be_alive
1498
1580
  folder.reload if folder.updated?
1499
1581
 
1500
- res = []
1582
+ res = new_bulk_response
1501
1583
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
1502
1584
  msg_set = folder.parse_msg_set(msg_set, uid: uid)
1503
1585
 
@@ -1513,17 +1595,26 @@ module RIMS
1513
1595
 
1514
1596
  if (msg_list.size > 0) then
1515
1597
  mailbox_size_server_response_multicast_push(mbox_id)
1516
- folder.server_response_fetch{|r| res << r }
1598
+ folder.server_response_fetch{|untagged_response|
1599
+ res << untagged_response
1600
+ yield(res.flush) if res.full?
1601
+ }
1517
1602
  res << "#{tag} OK [COPYUID #{mbox_id} #{src_uids.join(',')} #{dst_uids.join(',')}] COPY completed\r\n"
1518
1603
  else
1519
- folder.server_response_fetch{|r| res << r }
1604
+ folder.server_response_fetch{|untagged_response|
1605
+ res << untagged_response
1606
+ yield(res.flush) if res.full?
1607
+ }
1520
1608
  res << "#{tag} OK COPY completed\r\n"
1521
1609
  end
1522
1610
  else
1523
- folder.server_response_fetch{|r| res << r }
1611
+ folder.server_response_fetch{|untagged_response|
1612
+ res << untagged_response
1613
+ yield(res.flush) if res.full?
1614
+ }
1524
1615
  res << "#{tag} NO [TRYCREATE] not found a mailbox\r\n"
1525
1616
  end
1526
- yield(res)
1617
+ yield(res.flush)
1527
1618
  end
1528
1619
  imap_command_selected :copy, exclusive: true
1529
1620
 
@@ -1535,12 +1626,15 @@ module RIMS
1535
1626
  server_output_write.call([ "+ continue\r\n" ])
1536
1627
 
1537
1628
  server_response_thread = Thread.new{
1538
- @logger.info('idle server response thread start... ')
1629
+ res = new_bulk_response
1630
+ @logger.info('idle server response thread start...')
1539
1631
  folder.server_response_idle_wait{|server_response_list|
1540
- for server_response in server_response_list
1541
- @logger.debug("idle server response: #{server_response}") if @logger.debug?
1632
+ for untagged_response in server_response_list
1633
+ @logger.debug("idle server response: #{untagged_response}") if @logger.debug?
1634
+ res << untagged_response
1635
+ server_output_write.call(res.flush) if res.full?
1542
1636
  end
1543
- server_output_write.call(server_response_list)
1637
+ server_output_write.call(res.flush) unless res.empty?
1544
1638
  }
1545
1639
  @logger.info('idle server response thread terminated.')
1546
1640
  }
@@ -1553,23 +1647,23 @@ module RIMS
1553
1647
  server_response_thread.join
1554
1648
  end
1555
1649
 
1556
- res = []
1650
+ last_res = []
1557
1651
  if (line) then
1558
1652
  line.chomp!("\n")
1559
1653
  line.chomp!("\r")
1560
1654
  if (line.upcase == "DONE") then
1561
1655
  @logger.info('idle terminated.')
1562
- res << "#{tag} OK IDLE terminated\r\n"
1656
+ last_res << "#{tag} OK IDLE terminated\r\n"
1563
1657
  else
1564
1658
  @logger.warn('unexpected client response and idle terminated.')
1565
1659
  @logger.debug("unexpected client response data: #{line}") if @logger.debug?
1566
- res << "#{tag} BAD unexpected client response\r\n"
1660
+ last_res << "#{tag} BAD unexpected client response\r\n"
1567
1661
  end
1568
1662
  else
1569
1663
  @logger.warn('unexpected client connection close and idle terminated.')
1570
- res << "#{tag} BAD unexpected client connection close\r\n"
1664
+ last_res << "#{tag} BAD unexpected client connection close\r\n"
1571
1665
  end
1572
- yield(res)
1666
+ yield(last_res)
1573
1667
  end
1574
1668
  imap_command_selected :idle, exclusive: nil
1575
1669
  end
@@ -1579,6 +1673,7 @@ module RIMS
1579
1673
  @parent_decoder = parent_decoder
1580
1674
  @engine = engine
1581
1675
  @token = nil
1676
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder#initialize at #{self}") if @logger.debug?
1582
1677
  end
1583
1678
 
1584
1679
  def auth?
@@ -1589,43 +1684,57 @@ module RIMS
1589
1684
  ! @token.nil?
1590
1685
  end
1591
1686
 
1592
- def cleanup
1687
+ # `not_cleanup_parent' keyword argument is defined for MailDeliveryDecoder
1688
+ def cleanup(not_cleanup_parent: false)
1689
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder#cleanup at #{self}") if @logger.debug?
1690
+
1593
1691
  unless (@engine.nil?) then
1594
1692
  begin
1595
1693
  @engine.cleanup(@token)
1596
1694
  ensure
1597
1695
  @token = nil
1598
1696
  end
1697
+ end
1599
1698
 
1600
- begin
1601
- @engine.destroy
1602
- ensure
1603
- @engine = nil
1699
+ unless (not_cleanup_parent) then
1700
+ unless (@engine.nil?) then
1701
+ begin
1702
+ @engine.destroy
1703
+ ensure
1704
+ @engine = nil
1705
+ end
1604
1706
  end
1605
- end
1606
1707
 
1607
- unless (@parent_decoder.nil?) then
1608
- @parent_decoder.cleanup
1609
- @parent_decoder = nil
1708
+ unless (@parent_decoder.nil?) then
1709
+ @parent_decoder.cleanup
1710
+ @parent_decoder = nil
1711
+ end
1610
1712
  end
1611
1713
 
1612
1714
  nil
1613
1715
  end
1614
1716
 
1615
- def noop(tag, &block)
1616
- @engine.noop(@token, tag, &block)
1717
+ def noop(tag)
1718
+ ret_val = nil
1719
+ @engine.noop(@token, tag) {|res|
1720
+ for response in res
1721
+ ret_val = yield(response)
1722
+ end
1723
+ }
1724
+
1725
+ ret_val
1617
1726
  end
1618
1727
  imap_command :noop
1619
1728
 
1620
- def logout(tag)
1729
+ def logout(tag, &block)
1621
1730
  if (@token) then
1622
1731
  old_token = @token
1623
1732
  @token = nil
1624
1733
  @engine.cleanup(old_token)
1625
1734
  end
1626
1735
 
1627
- @next_decoder = LogoutDecoder.new(self)
1628
- yield(make_logout_response(tag))
1736
+ @next_decoder = LogoutDecoder.new(self, @logger)
1737
+ make_logout_response(tag, &block)
1629
1738
  end
1630
1739
  imap_command :logout
1631
1740
 
@@ -1633,7 +1742,9 @@ module RIMS
1633
1742
  ret_val = nil
1634
1743
  old_token = @token
1635
1744
  @token = @engine.select(old_token, tag, mbox_name) {|res|
1636
- ret_val = yield(res)
1745
+ for response in res
1746
+ ret_val = yield(response)
1747
+ end
1637
1748
  }
1638
1749
 
1639
1750
  ret_val
@@ -1644,134 +1755,225 @@ module RIMS
1644
1755
  ret_val = nil
1645
1756
  old_token = @token
1646
1757
  @token = @engine.examine(old_token, tag, mbox_name) {|res|
1647
- ret_val = yield(res)
1758
+ for response in res
1759
+ ret_val = yield(response)
1760
+ end
1648
1761
  }
1649
1762
 
1650
1763
  ret_val
1651
1764
  end
1652
1765
  imap_command :examine
1653
1766
 
1654
- def create(tag, mbox_name, &block)
1655
- @engine.create(@token, tag, mbox_name, &block)
1767
+ def create(tag, mbox_name)
1768
+ ret_val = nil
1769
+ @engine.create(@token, tag, mbox_name) {|res|
1770
+ for response in res
1771
+ ret_val = yield(response)
1772
+ end
1773
+ }
1774
+
1775
+ ret_val
1656
1776
  end
1657
1777
  imap_command :create
1658
1778
 
1659
- def delete(tag, mbox_name, &block)
1660
- @engine.delete(@token, tag, mbox_name, &block)
1779
+ def delete(tag, mbox_name)
1780
+ ret_val = nil
1781
+ @engine.delete(@token, tag, mbox_name) {|res|
1782
+ for response in res
1783
+ ret_val = yield(response)
1784
+ end
1785
+ }
1786
+
1787
+ ret_val
1661
1788
  end
1662
1789
  imap_command :delete
1663
1790
 
1664
- def rename(tag, src_name, dst_name, &block)
1665
- @engine.rename(@token, tag, src_name, dst_name, &block)
1791
+ def rename(tag, src_name, dst_name)
1792
+ ret_val = nil
1793
+ @engine.rename(@token, tag, src_name, dst_name) {|res|
1794
+ for response in res
1795
+ ret_val = yield(response)
1796
+ end
1797
+ }
1798
+
1799
+ ret_val
1666
1800
  end
1667
1801
  imap_command :rename
1668
1802
 
1669
- def subscribe(tag, mbox_name, &block)
1670
- @engine.subscribe(@token, tag, mbox_name, &block)
1803
+ def subscribe(tag, mbox_name)
1804
+ ret_val = nil
1805
+ @engine.subscribe(@token, tag, mbox_name) {|res|
1806
+ for response in res
1807
+ ret_val = yield(response)
1808
+ end
1809
+ }
1810
+
1811
+ ret_val
1671
1812
  end
1672
1813
  imap_command :subscribe
1673
1814
 
1674
- def unsubscribe(tag, mbox_name, &block)
1675
- @engine.unsubscribe(@token, tag, mbox_name, &block)
1815
+ def unsubscribe(tag, mbox_name)
1816
+ ret_val = nil
1817
+ @engine.unsubscribe(@token, tag, mbox_name) {|res|
1818
+ for response in res
1819
+ ret_val = yield(response)
1820
+ end
1821
+ }
1822
+
1823
+ ret_val
1676
1824
  end
1677
1825
  imap_command :unsubscribe
1678
1826
 
1679
- def list(tag, ref_name, mbox_name, &block)
1680
- @engine.list(@token, tag, ref_name, mbox_name, &block)
1827
+ def list(tag, ref_name, mbox_name)
1828
+ ret_val = nil
1829
+ @engine.list(@token, tag, ref_name, mbox_name) {|res|
1830
+ for response in res
1831
+ ret_val = yield(response)
1832
+ end
1833
+ }
1834
+
1835
+ ret_val
1681
1836
  end
1682
1837
  imap_command :list
1683
1838
 
1684
- def lsub(tag, ref_name, mbox_name, &block)
1685
- @engine.lsub(@token, tag, ref_name, mbox_name, &block)
1839
+ def lsub(tag, ref_name, mbox_name)
1840
+ ret_val = nil
1841
+ @engine.lsub(@token, tag, ref_name, mbox_name) {|res|
1842
+ for response in res
1843
+ ret_val = yield(response)
1844
+ end
1845
+ }
1846
+
1847
+ ret_val
1686
1848
  end
1687
1849
  imap_command :lsub
1688
1850
 
1689
- def status(tag, mbox_name, data_item_group, &block)
1690
- @engine.status(@token, tag, mbox_name, data_item_group, &block)
1851
+ def status(tag, mbox_name, data_item_group)
1852
+ ret_val = nil
1853
+ @engine.status(@token, tag, mbox_name, data_item_group) {|res|
1854
+ for response in res
1855
+ ret_val = yield(response)
1856
+ end
1857
+ }
1858
+
1859
+ ret_val
1691
1860
  end
1692
1861
  imap_command :status
1693
1862
 
1694
- def append(tag, mbox_name, *opt_args, msg_text, &block)
1695
- @engine.append(@token, tag, mbox_name, *opt_args, msg_text, &block)
1863
+ def append(tag, mbox_name, *opt_args, msg_text)
1864
+ ret_val = nil
1865
+ @engine.append(@token, tag, mbox_name, *opt_args, msg_text) {|res|
1866
+ for response in res
1867
+ ret_val = yield(response)
1868
+ end
1869
+ }
1870
+
1871
+ ret_val
1696
1872
  end
1697
1873
  imap_command :append
1698
1874
 
1699
- def check(tag, &block)
1700
- @engine.check(@token, tag, &block)
1875
+ def check(tag)
1876
+ ret_val = nil
1877
+ @engine.check(@token, tag) {|res|
1878
+ for response in res
1879
+ ret_val = yield(response)
1880
+ end
1881
+ }
1882
+
1883
+ ret_val
1701
1884
  end
1702
1885
  imap_command :check
1703
1886
 
1704
1887
  def close(tag, &block)
1888
+ ret_val = nil
1705
1889
  old_token = @token
1706
1890
  @token = nil
1707
-
1708
- yield response_stream(tag) {|res|
1709
- @engine.close(old_token, tag) {|bulk_res|
1710
- for r in bulk_res
1711
- res << r
1712
- end
1713
- }
1891
+ @engine.close(old_token, tag) {|res|
1892
+ for response in res
1893
+ ret_val = yield(response)
1894
+ end
1714
1895
  }
1896
+
1897
+ ret_val
1715
1898
  end
1716
1899
  imap_command :close
1717
1900
 
1718
1901
  def expunge(tag)
1719
- yield response_stream(tag) {|res|
1720
- @engine.expunge(@token, tag) {|bulk_res|
1721
- for r in bulk_res
1722
- res << r
1723
- end
1724
- }
1902
+ ret_val = nil
1903
+ @engine.expunge(@token, tag) {|res|
1904
+ for response in res
1905
+ ret_val = yield(response)
1906
+ end
1725
1907
  }
1908
+
1909
+ ret_val
1726
1910
  end
1727
1911
  imap_command :expunge
1728
1912
 
1729
1913
  def search(tag, *cond_args, uid: false)
1730
- yield response_stream(tag) {|res|
1731
- @engine.search(@token, tag, *cond_args, uid: uid) {|bulk_res|
1732
- for r in bulk_res
1733
- res << r
1734
- end
1735
- }
1914
+ ret_val = nil
1915
+ @engine.search(@token, tag, *cond_args, uid: uid) {|res|
1916
+ for response in res
1917
+ ret_val = yield(response)
1918
+ end
1736
1919
  }
1920
+
1921
+ ret_val
1737
1922
  end
1738
1923
  imap_command :search
1739
1924
 
1740
1925
  def fetch(tag, msg_set, data_item_group, uid: false)
1741
- yield response_stream(tag) {|res|
1742
- @engine.fetch(@token, tag, msg_set, data_item_group, uid: uid) {|bulk_res|
1743
- for r in bulk_res
1744
- res << r
1745
- end
1746
- }
1926
+ ret_val = nil
1927
+ @engine.fetch(@token, tag, msg_set, data_item_group, uid: uid) {|res|
1928
+ for response in res
1929
+ ret_val = yield(response)
1930
+ end
1747
1931
  }
1932
+
1933
+ ret_val
1748
1934
  end
1749
1935
  imap_command :fetch
1750
1936
 
1751
1937
  def store(tag, msg_set, data_item_name, data_item_value, uid: false)
1752
- yield response_stream(tag) {|res|
1753
- @engine.store(@token, tag, msg_set, data_item_name, data_item_value, uid: uid) {|bulk_res|
1754
- for r in bulk_res
1755
- res << r
1756
- end
1757
- }
1938
+ ret_val = nil
1939
+ @engine.store(@token, tag, msg_set, data_item_name, data_item_value, uid: uid) {|res|
1940
+ for response in res
1941
+ ret_val = yield(response)
1942
+ end
1758
1943
  }
1944
+
1945
+ ret_val
1759
1946
  end
1760
1947
  imap_command :store
1761
1948
 
1762
1949
  def copy(tag, msg_set, mbox_name, uid: false, &block)
1763
- @engine.copy(@token, tag, msg_set, mbox_name, uid: uid, &block)
1950
+ ret_val = nil
1951
+ @engine.copy(@token, tag, msg_set, mbox_name, uid: uid) {|res|
1952
+ for response in res
1953
+ ret_val = yield(response)
1954
+ end
1955
+ }
1956
+
1957
+ ret_val
1764
1958
  end
1765
1959
  imap_command :copy
1766
1960
 
1767
1961
  def idle(tag, client_input_gets, server_output_write, connection_timer, &block)
1768
- @engine.idle(@token, tag, client_input_gets, server_output_write, connection_timer, &block)
1962
+ ret_val = nil
1963
+ @engine.idle(@token, tag, client_input_gets, server_output_write, connection_timer) {|res|
1964
+ for response in res
1965
+ ret_val = yield(response)
1966
+ end
1967
+ }
1968
+
1969
+ ret_val
1769
1970
  end
1770
1971
  imap_command :idle
1771
1972
  end
1772
1973
 
1773
1974
  # alias
1774
- Decoder::Engine = UserMailboxDecoder::Engine
1975
+ Decoder::Engine = UserMailboxDecoder::Engine
1976
+ Decoder::BulkResponse = UserMailboxDecoder::BulkResponse
1775
1977
 
1776
1978
  def Decoder.encode_delivery_target_mailbox(username, mbox_name)
1777
1979
  "b64user-mbox #{Protocol.encode_base64(username)} #{mbox_name}"
@@ -1793,6 +1995,7 @@ module RIMS
1793
1995
  @auth = auth
1794
1996
  @last_user_cache_key_username = nil
1795
1997
  @last_user_cache_value_engine = nil
1998
+ @logger.debug("RIMS::Protocol::MailDeliveryDecoder#initialize at #{self}") if @logger.debug?
1796
1999
  end
1797
2000
 
1798
2001
  def engine_cached?(username)
@@ -1840,6 +2043,8 @@ module RIMS
1840
2043
  end
1841
2044
 
1842
2045
  def cleanup
2046
+ @logger.debug("RIMS::Protocol::MailDeliveryDecoder#cleanup at #{self}") if @logger.debug?
2047
+
1843
2048
  release_engine_cache
1844
2049
  @drb_services = nil unless @drb_services.nil?
1845
2050
  @auth = nil unless @auth.nil?
@@ -1852,9 +2057,9 @@ module RIMS
1852
2057
  nil
1853
2058
  end
1854
2059
 
1855
- def logout(tag)
1856
- @next_decoder = LogoutDecoder.new(self)
1857
- yield(make_logout_response(tag))
2060
+ def logout(tag, &block)
2061
+ @next_decoder = LogoutDecoder.new(self, @logger)
2062
+ make_logout_response(tag, &block)
1858
2063
  end
1859
2064
  imap_command :logout
1860
2065
 
@@ -1862,85 +2067,87 @@ module RIMS
1862
2067
  private :standard_capability
1863
2068
 
1864
2069
  def capability(tag)
1865
- standard_capability(tag) {|res|
1866
- yield res.map{|line|
1867
- if (line.start_with? '* CAPABILITY ') then
1868
- line.strip + " X-RIMS-MAIL-DELIVERY-USER\r\n"
1869
- else
1870
- line
1871
- end
1872
- }
2070
+ standard_capability(tag) {|response|
2071
+ if (response.start_with? '* CAPABILITY ') then
2072
+ yield(response.strip + " X-RIMS-MAIL-DELIVERY-USER\r\n")
2073
+ else
2074
+ yield(response)
2075
+ end
1873
2076
  }
1874
2077
  end
1875
2078
  imap_command :capability
1876
2079
 
1877
2080
  def make_not_allowed_command_response(tag)
1878
- [ "#{tag} NO not allowed command on mail delivery user\r\n" ]
2081
+ yield("#{tag} NO not allowed command on mail delivery user\r\n")
1879
2082
  end
1880
2083
  private :make_not_allowed_command_response
1881
2084
 
1882
- def select(tag, mbox_name)
1883
- yield(make_not_allowed_command_response(tag))
2085
+ def select(tag, mbox_name, &block)
2086
+ make_not_allowed_command_response(tag, &block)
1884
2087
  end
1885
2088
  imap_command :select
1886
2089
 
1887
- def examine(tag, mbox_name)
1888
- yield(make_not_allowed_command_response(tag))
2090
+ def examine(tag, mbox_name, &block)
2091
+ make_not_allowed_command_response(tag, &block)
1889
2092
  end
1890
2093
  imap_command :examine
1891
2094
 
1892
- def create(tag, mbox_name)
1893
- yield(make_not_allowed_command_response(tag))
2095
+ def create(tag, mbox_name, &block)
2096
+ make_not_allowed_command_response(tag, &block)
1894
2097
  end
1895
2098
  imap_command :create
1896
2099
 
1897
- def delete(tag, mbox_name)
1898
- yield(make_not_allowed_command_response(tag))
2100
+ def delete(tag, mbox_name, &block)
2101
+ make_not_allowed_command_response(tag, &block)
1899
2102
  end
1900
2103
  imap_command :delete
1901
2104
 
1902
- def rename(tag, src_name, dst_name)
1903
- yield(make_not_allowed_command_response(tag))
2105
+ def rename(tag, src_name, dst_name, &block)
2106
+ make_not_allowed_command_response(tag, &block)
1904
2107
  end
1905
2108
  imap_command :rename
1906
2109
 
1907
- def subscribe(tag, mbox_name)
1908
- yield(make_not_allowed_command_response(tag))
2110
+ def subscribe(tag, mbox_name, &block)
2111
+ make_not_allowed_command_response(tag, &block)
1909
2112
  end
1910
2113
  imap_command :subscribe
1911
2114
 
1912
- def unsubscribe(tag, mbox_name)
1913
- yield(make_not_allowed_command_response(tag))
2115
+ def unsubscribe(tag, mbox_name, &block)
2116
+ make_not_allowed_command_response(tag, &block)
1914
2117
  end
1915
2118
  imap_command :unsubscribe
1916
2119
 
1917
- def list(tag, ref_name, mbox_name)
1918
- yield(make_not_allowed_command_response(tag))
2120
+ def list(tag, ref_name, mbox_name, &block)
2121
+ make_not_allowed_command_response(tag, &block)
1919
2122
  end
1920
2123
  imap_command :list
1921
2124
 
1922
- def lsub(tag, ref_name, mbox_name)
1923
- yield(make_not_allowed_command_response(tag))
2125
+ def lsub(tag, ref_name, mbox_name, &block)
2126
+ make_not_allowed_command_response(tag, &block)
1924
2127
  end
1925
2128
  imap_command :lsub
1926
2129
 
1927
- def status(tag, mbox_name, data_item_group)
1928
- yield(make_not_allowed_command_response(tag))
2130
+ def status(tag, mbox_name, data_item_group, &block)
2131
+ make_not_allowed_command_response(tag, &block)
1929
2132
  end
1930
2133
  imap_command :status
1931
2134
 
1932
- def deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, res)
2135
+ def deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine)
1933
2136
  user_decoder = UserMailboxDecoder.new(self, engine, @auth, @logger)
1934
- user_decoder.append(tag, mbox_name, *opt_args, msg_text) {|append_response|
1935
- if (append_response.last.split(' ', 3)[1] == 'OK') then
2137
+ begin
2138
+ last_response = nil
2139
+ user_decoder.append(tag, mbox_name, *opt_args, msg_text) {|response|
2140
+ last_response = response
2141
+ yield(response)
2142
+ }
2143
+ if (last_response.split(' ', 3)[1] == 'OK') then
1936
2144
  @logger.info("message delivery: successed to deliver #{msg_text.bytesize} octets message.")
1937
2145
  else
1938
2146
  @logger.info("message delivery: failed to deliver message.")
1939
2147
  end
1940
- for response_data in append_response
1941
- res << response_data
1942
- end
1943
- }
2148
+ ensure
2149
+ user_decoder.cleanup(not_cleanup_parent: true)
2150
+ end
1944
2151
  end
1945
2152
  private :deliver_to_user
1946
2153
 
@@ -1950,62 +2157,65 @@ module RIMS
1950
2157
 
1951
2158
  if (@auth.user? username) then
1952
2159
  if (engine_cached? username) then
1953
- res = []
1954
2160
  engine = engine_cache(username)
1955
- deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, res)
2161
+ deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine) {|response|
2162
+ yield(response)
2163
+ }
1956
2164
  else
1957
- res = response_stream(tag) {|stream_res|
1958
- engine = store_engine_cache(username) {
1959
- self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|msg| stream_res << msg << :flush }
2165
+ engine = store_engine_cache(username) {
2166
+ self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|untagged_response|
2167
+ yield(untagged_response)
2168
+ yield(:flush)
1960
2169
  }
1961
- deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, stream_res)
2170
+ }
2171
+ deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine) {|response|
2172
+ yield(response)
1962
2173
  }
1963
2174
  end
1964
- yield(res)
1965
2175
  else
1966
2176
  @logger.info('message delivery: not found a user.')
1967
- yield([ "#{tag} NO not found a user and couldn't deliver a message to the user's mailbox\r\n" ])
2177
+ yield("#{tag} NO not found a user and couldn't deliver a message to the user's mailbox\r\n")
1968
2178
  end
1969
2179
  end
1970
2180
  imap_command :append
1971
2181
 
1972
- def check(tag)
1973
- yield(make_not_allowed_command_response(tag))
2182
+ def check(tag, &block)
2183
+ make_not_allowed_command_response(tag, &block)
1974
2184
  end
1975
2185
  imap_command :check
1976
2186
 
1977
- def close(tag)
1978
- yield(make_not_allowed_command_response(tag))
2187
+ def close(tag, &block)
2188
+ make_not_allowed_command_response(tag, &block)
1979
2189
  end
1980
2190
  imap_command :close
1981
2191
 
1982
- def expunge(tag)
1983
- yield(make_not_allowed_command_response(tag))
2192
+ def expunge(tag, &block)
2193
+ make_not_allowed_command_response(tag, &block)
1984
2194
  end
1985
2195
  imap_command :expunge
1986
2196
 
1987
- def search(tag, *cond_args, uid: false)
1988
- yield(make_not_allowed_command_response(tag))
2197
+ def search(tag, *cond_args, uid: false, &block)
2198
+ make_not_allowed_command_response(tag, &block)
1989
2199
  end
1990
2200
  imap_command :search
1991
2201
 
1992
- def fetch(tag, msg_set, data_item_group, uid: false)
1993
- yield(make_not_allowed_command_response(tag))
2202
+ def fetch(tag, msg_set, data_item_group, uid: false, &block)
2203
+ make_not_allowed_command_response(tag, &block)
1994
2204
  end
1995
2205
  imap_command :fetch
1996
2206
 
1997
- def store(tag, msg_set, data_item_name, data_item_value, uid: false)
1998
- yield(make_not_allowed_command_response(tag))
2207
+ def store(tag, msg_set, data_item_name, data_item_value, uid: false, &block)
2208
+ make_not_allowed_command_response(tag, &block)
1999
2209
  end
2000
2210
  imap_command :store
2001
2211
 
2002
- def copy(tag, msg_set, mbox_name, uid: false)
2003
- yield(make_not_allowed_command_response(tag))
2212
+ def copy(tag, msg_set, mbox_name, uid: false, &block)
2213
+ make_not_allowed_command_response(tag, &block)
2004
2214
  end
2005
2215
  imap_command :copy
2006
2216
 
2007
- def idle(tag, client_input_gets, server_output_write, connection_timer)
2008
- yield(make_not_allowed_command_response(tag))
2217
+ def idle(tag, client_input_gets, server_output_write, connection_timer, &block)
2218
+ make_not_allowed_command_response(tag, &block)
2009
2219
  end
2010
2220
  imap_command :idle
2011
2221
  end