rims 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
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