rims 0.2.6 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
 
@@ -4,6 +4,27 @@ module RIMS
4
4
  class ProtocolError < Error
5
5
  end
6
6
 
7
+ class ReadSizeError < ProtocolError
8
+ end
9
+
10
+ class LineTooLongError < ReadSizeError
11
+ end
12
+
13
+ class RecoverableReadSizeError < ReadSizeError
14
+ def initialize(message=nil, command_tag=nil, **kw_args)
15
+ super(message, **kw_args)
16
+ @command_tag = command_tag
17
+ end
18
+
19
+ attr_reader :command_tag
20
+ end
21
+
22
+ class LiteralSizeTooLargeError < RecoverableReadSizeError
23
+ end
24
+
25
+ class CommandSizeTooLargeError < RecoverableReadSizeError
26
+ end
27
+
7
28
  class SyntaxError < ProtocolError
8
29
  end
9
30
 
@@ -57,14 +78,14 @@ module RIMS
57
78
  end
58
79
  module_function :decode_base64
59
80
 
60
- autoload :FetchBody, 'rims/protocol/parser'
61
- autoload :RequestReader, 'rims/protocol/parser'
81
+ autoload :FetchBody, 'rims/protocol/parser'
82
+ autoload :RequestReader, 'rims/protocol/parser'
62
83
  autoload :AuthenticationReader, 'rims/protocol/parser'
63
- autoload :SearchParser, 'rims/protocol/parser'
64
- autoload :FetchParser, 'rims/protocol/parser'
65
- autoload :ConnectionLimits, 'rims/protocol/connection'
66
- autoload :ConnectionTimer, 'rims/protocol/connection'
67
- autoload :Decoder, 'rims/protocol/decoder'
84
+ autoload :SearchParser, 'rims/protocol/parser'
85
+ autoload :FetchParser, 'rims/protocol/parser'
86
+ autoload :ConnectionLimits, 'rims/protocol/connection'
87
+ autoload :ConnectionTimer, 'rims/protocol/connection'
88
+ autoload :Decoder, 'rims/protocol/decoder'
68
89
 
69
90
  def body(symbol: nil, option: nil, section: nil, section_list: nil, partial_origin: nil, partial_size: nil)
70
91
  FetchBody.new(symbol, option, section, section_list, partial_origin, partial_size)
@@ -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,125 +22,174 @@ 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
- input_gets = input.method(:gets)
26
- output_write = lambda{|res|
27
- last_line = nil
28
- for data in res
40
+ output_write = lambda{|data|
41
+ begin
29
42
  if (data == :flush) then
30
43
  output.flush
31
44
  else
32
45
  logger.debug("response data: #{Protocol.io_data_log(data)}") if logger.debug?
33
46
  output << data
34
- last_line = data
35
47
  end
48
+ rescue
49
+ logger.error('response write error.')
50
+ logging_error_chain($!, logger)
51
+ raise
52
+ end
53
+ }
54
+ server_output_write = lambda{|res|
55
+ for data in res
56
+ output_write.call(data)
36
57
  end
37
58
  output.flush
38
59
 
39
- last_line
60
+ nil
40
61
  }
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
62
+ response_write = lambda{|response|
63
+ output_write.call(response)
64
+ output.flush
65
+ logger.info("server response: #{response.strip}")
66
+ }
67
+ apply_imap_command = lambda{|name, *args, uid: false|
68
+ last_line = nil
69
+ if (uid) then
70
+ decoder.__send__(name, *args, uid: true) {|response|
71
+ output_write.call(response)
72
+ last_line = response if (response.is_a? String)
73
+ }
74
+ else
75
+ decoder.__send__(name, *args) {|response|
76
+ output_write.call(response)
77
+ last_line = response if (response.is_a? String)
78
+ }
49
79
  end
80
+ output.flush
81
+ logger.info("server response: #{last_line.strip}") if last_line
50
82
  }
51
83
 
52
- decoder.ok_greeting{|res| response_write.call(res) }
84
+ apply_imap_command.call(:ok_greeting)
53
85
 
54
86
  conn_timer = ConnectionTimer.new(limits, input.to_io)
55
- request_reader = RequestReader.new(input, output, logger)
87
+ request_reader = decoder.make_requrest_reader(input, output)
88
+ input_gets = request_reader.method(:gets)
56
89
 
57
- until (conn_timer.command_wait_timeout?)
58
- conn_timer.command_wait or break
90
+ begin
91
+ until (conn_timer.command_wait_timeout?)
92
+ conn_timer.command_wait or break
59
93
 
60
- begin
61
- atom_list = request_reader.read_command
62
- rescue
63
- logger.error('invalid client command.')
64
- Error.trace_error_chain($!) do |exception|
65
- logger.error(exception)
94
+ begin
95
+ atom_list = request_reader.read_command
96
+ rescue LineTooLongError
97
+ raise
98
+ rescue LiteralSizeTooLargeError
99
+ logger.error('literal size too large error.')
100
+ logging_error_chain($!, logger)
101
+ response_write.call("#{request_reader.command_tag || '*'} BAD literal size too large\r\n")
102
+ next
103
+ rescue CommandSizeTooLargeError
104
+ logger.error('command size too large error.')
105
+ logging_error_chain($!, logger)
106
+ response_write.call("#{request_reader.command_tag || '*'} BAD command size too large\r\n")
107
+ next
108
+ rescue
109
+ logger.error('invalid client command.')
110
+ logging_error_chain($!, logger)
111
+ response_write.call("#{request_reader.command_tag || '*'} BAD client command syntax error\r\n")
112
+ next
66
113
  end
67
- response_write.call([ "* BAD client command syntax error\r\n" ])
68
- next
69
- end
70
114
 
71
- break unless atom_list
115
+ break unless atom_list
72
116
 
73
- tag, command, *opt_args = atom_list
74
- normalized_command = imap_command_normalize(command)
75
- logger.info("client command: #{tag} #{command}")
76
- if (logger.debug?) then
77
- case (normalized_command)
78
- when 'LOGIN'
79
- log_opt_args = opt_args.dup
80
- log_opt_args[-1] = '********'
81
- when 'AUTHENTICATE'
82
- if (opt_args[1]) then
117
+ tag, command, *opt_args = atom_list
118
+ normalized_command = imap_command_normalize(command)
119
+ logger.info("client command: #{tag} #{command}")
120
+ if (logger.debug?) then
121
+ case (normalized_command)
122
+ when 'LOGIN'
83
123
  log_opt_args = opt_args.dup
84
- log_opt_args[1] = '********'
124
+ log_opt_args[-1] = '********'
125
+ when 'AUTHENTICATE'
126
+ if (opt_args[1]) then
127
+ log_opt_args = opt_args.dup
128
+ log_opt_args[1] = '********'
129
+ else
130
+ log_opt_args = opt_args
131
+ end
85
132
  else
86
133
  log_opt_args = opt_args
87
134
  end
88
- else
89
- log_opt_args = opt_args
135
+ logger.debug("client command parameter: #{log_opt_args.inspect}")
90
136
  end
91
- logger.debug("client command parameter: #{log_opt_args.inspect}")
92
- end
93
137
 
94
- begin
95
- if (name = IMAP_CMDs[normalized_command]) then
96
- case (name)
97
- when :uid
98
- unless (opt_args.empty?) then
99
- uid_command, *uid_args = opt_args
100
- logger.info("uid command: #{uid_command}")
101
- logger.debug("uid parameter: #{uid_args}") if logger.debug?
102
- 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) }
138
+ begin
139
+ if (name = IMAP_CMDs[normalized_command]) then
140
+ case (name)
141
+ when :uid
142
+ unless (opt_args.empty?) then
143
+ uid_command, *uid_args = opt_args
144
+ logger.info("uid command: #{uid_command}")
145
+ logger.debug("uid parameter: #{uid_args}") if logger.debug?
146
+ if (uid_name = UID_CMDs[imap_command_normalize(uid_command)]) then
147
+ apply_imap_command.call(uid_name, tag, *uid_args, uid: true)
148
+ else
149
+ logger.error("unknown uid command: #{uid_command}")
150
+ response_write.call("#{tag} BAD unknown uid command\r\n")
151
+ end
104
152
  else
105
- logger.error("unknown uid command: #{uid_command}")
106
- response_write.call([ "#{tag} BAD unknown uid command\r\n" ])
153
+ logger.error('empty uid parameter.')
154
+ response_write.call("#{tag} BAD empty uid parameter\r\n")
107
155
  end
156
+ when :authenticate
157
+ apply_imap_command.call(:authenticate, tag, input_gets, server_output_write, *opt_args)
158
+ when :idle
159
+ apply_imap_command.call(:idle, tag, input_gets, server_output_write, conn_timer, *opt_args)
108
160
  else
109
- logger.error('empty uid parameter.')
110
- response_write.call([ "#{tag} BAD empty uid parameter\r\n" ])
161
+ apply_imap_command.call(name, tag, *opt_args)
111
162
  end
112
- when :authenticate
113
- decoder.authenticate(tag, input_gets, output_write, *opt_args) {|res| response_write.call(res) }
114
- when :idle
115
- decoder.idle(tag, input_gets, output_write, conn_timer, *opt_args) {|res| response_write.call(res) }
116
163
  else
117
- decoder.__send__(name, tag, *opt_args) {|res| response_write.call(res) }
164
+ logger.error("unknown command: #{command}")
165
+ response_write.call("#{tag} BAD unknown command\r\n")
118
166
  end
119
- else
120
- logger.error("unknown command: #{command}")
121
- response_write.call([ "#{tag} BAD unknown command\r\n" ])
167
+ rescue LineTooLongError
168
+ raise
169
+ rescue
170
+ logger.error('unexpected error.')
171
+ logging_error_chain($!, logger)
172
+ response_write.call("#{tag} BAD unexpected error\r\n")
122
173
  end
123
- rescue
124
- logger.error('unexpected error.')
125
- Error.trace_error_chain($!) do |exception|
126
- logger.error(exception)
174
+
175
+ if (normalized_command == 'LOGOUT') then
176
+ break
127
177
  end
128
- response_write.call([ "#{tag} BAD unexpected error\r\n" ])
129
- end
130
178
 
131
- if (normalized_command == 'LOGOUT') then
132
- break
179
+ decoder = decoder.next_decoder
133
180
  end
134
-
135
- decoder = decoder.next_decoder
136
- end
137
-
138
- if (conn_timer.command_wait_timeout?) then
139
- if (limits.command_wait_timeout_seconds > 0) then
140
- response_write.call([ "* BYE server autologout: idle for too long\r\n" ])
141
- else
142
- response_write.call([ "* BYE server autologout: shutdown\r\n" ])
181
+ rescue LineTooLongError
182
+ logger.error('line too long error.')
183
+ logging_error_chain($!, logger)
184
+ response_write.call("* BAD line too long\r\n")
185
+ response_write.call("* BYE server autologout: connection terminated\r\n")
186
+ else
187
+ if (conn_timer.command_wait_timeout?) then
188
+ if (limits.command_wait_timeout_seconds > 0) then
189
+ response_write.call("* BYE server autologout: idle for too long\r\n")
190
+ else
191
+ response_write.call("* BYE server autologout: shutdown\r\n")
192
+ end
143
193
  end
144
194
  end
145
195
 
@@ -157,25 +207,10 @@ module RIMS
157
207
 
158
208
  attr_reader :next_decoder
159
209
 
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
- }
210
+ def logging_error_chain(error)
211
+ self.class.logging_error_chain(error, @logger)
177
212
  end
178
- private :response_stream
213
+ private :logging_error_chain
179
214
 
180
215
  def guard_error(imap_command, tag, *args, **kw_args, &block)
181
216
  begin
@@ -184,21 +219,21 @@ module RIMS
184
219
  else
185
220
  __send__(imap_command, tag, *args, **kw_args, &block)
186
221
  end
222
+ rescue LineTooLongError
223
+ raise
187
224
  rescue SyntaxError
188
225
  @logger.error('client command syntax error.')
189
- @logger.error($!)
190
- yield([ "#{tag} BAD client command syntax error\r\n" ])
226
+ logging_error_chain($!)
227
+ yield("#{tag} BAD client command syntax error\r\n")
191
228
  rescue ArgumentError
192
229
  @logger.error('invalid command parameter.')
193
- @logger.error($!)
194
- yield([ "#{tag} BAD invalid command parameter\r\n" ])
230
+ logging_error_chain($!)
231
+ yield("#{tag} BAD invalid command parameter\r\n")
195
232
  rescue
196
233
  raise if ($!.class.name =~ /AssertionFailedError/)
197
234
  @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" ])
235
+ logging_error_chain($!)
236
+ yield("#{tag} BAD internal server error\r\n")
202
237
  end
203
238
  end
204
239
  private :guard_error
@@ -256,8 +291,7 @@ module RIMS
256
291
  end
257
292
  private :imap_command
258
293
 
259
- def make_engine_and_recovery_if_needed(drb_services, username,
260
- logger: Logger.new(STDOUT))
294
+ def make_engine_and_recovery_if_needed(drb_services, username, logger: Logger.new(STDOUT))
261
295
  unique_user_id = Authentication.unique_user_id(username)
262
296
  logger.debug("unique user ID: #{username} -> #{unique_user_id}") if logger.debug?
263
297
 
@@ -265,7 +299,7 @@ module RIMS
265
299
  engine = drb_services[:engine, unique_user_id]
266
300
 
267
301
  begin
268
- engine.recovery_if_needed(username) {|msg| yield(msg) }
302
+ engine.recovery_if_needed(username) {|response| yield(response) }
269
303
  rescue
270
304
  engine.destroy
271
305
  raise
@@ -276,14 +310,13 @@ module RIMS
276
310
  end
277
311
 
278
312
  def make_logout_response(tag)
279
- [ "* BYE server logout\r\n",
280
- "#{tag} OK LOGOUT completed\r\n"
281
- ]
313
+ yield("* BYE server logout\r\n")
314
+ yield("#{tag} OK LOGOUT completed\r\n")
282
315
  end
283
316
  private :make_logout_response
284
317
 
285
318
  def ok_greeting
286
- yield([ "* OK RIMS v#{VERSION} IMAP4rev1 service ready.\r\n" ])
319
+ yield("* OK RIMS v#{VERSION} IMAP4rev1 service ready.\r\n")
287
320
  end
288
321
 
289
322
  # common IMAP command
@@ -292,10 +325,8 @@ module RIMS
292
325
  def capability(tag)
293
326
  capability_list = %w[ IMAP4rev1 UIDPLUS IDLE ]
294
327
  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)
328
+ yield("* CAPABILITY #{capability_list.join(' ')}\r\n")
329
+ yield("#{tag} OK CAPABILITY completed\r\n")
299
330
  end
300
331
  imap_command :capability
301
332
  end
@@ -326,10 +357,24 @@ module RIMS
326
357
  end
327
358
 
328
359
  def initialize(drb_services, auth, logger,
329
- mail_delivery_user: Service::DEFAULT_CONFIG.mail_delivery_user)
360
+ mail_delivery_user: Service::DEFAULT_CONFIG.mail_delivery_user,
361
+ line_length_limit: Service::DEFAULT_CONFIG.protocol_line_length_limit,
362
+ literal_size_limit: Service::DEFAULT_CONFIG.protocol_literal_size_limit,
363
+ command_size_limit: Service::DEFAULT_CONFIG.protocol_command_size_limit)
330
364
  super(auth, logger)
331
365
  @drb_services = drb_services
332
366
  @mail_delivery_user = mail_delivery_user
367
+ @line_length_limit = line_length_limit
368
+ @literal_size_limit = literal_size_limit
369
+ @command_size_limit = command_size_limit
370
+ @logger.debug("RIMS::Protocol::InitialDecoder#initialize at #{self}") if @logger.debug?
371
+ end
372
+
373
+ def make_requrest_reader(input, output)
374
+ RequestReader.new(input, output, @logger,
375
+ line_length_limit: @line_length_limit,
376
+ literal_size_limit: @literal_size_limit,
377
+ command_size_limit: @command_size_limit)
333
378
  end
334
379
 
335
380
  def auth?
@@ -341,22 +386,23 @@ module RIMS
341
386
  end
342
387
 
343
388
  def cleanup
389
+ @logger.debug("RIMS::Protocol::InitialDecoder#cleanup at #{self}") if @logger.debug?
344
390
  nil
345
391
  end
346
392
 
347
393
  def make_not_authenticated_response(tag)
348
- [ "#{tag} NO not authenticated\r\n" ]
394
+ yield("#{tag} NO not authenticated\r\n")
349
395
  end
350
396
  private :make_not_authenticated_response
351
397
 
352
398
  def noop(tag)
353
- yield([ "#{tag} OK NOOP completed\r\n" ])
399
+ yield("#{tag} OK NOOP completed\r\n")
354
400
  end
355
401
  imap_command :noop
356
402
 
357
- def logout(tag)
358
- @next_decoder = LogoutDecoder.new(self)
359
- yield(make_logout_response(tag))
403
+ def logout(tag, &block)
404
+ @next_decoder = LogoutDecoder.new(self, @logger)
405
+ make_logout_response(tag, &block)
360
406
  end
361
407
  imap_command :logout
362
408
 
@@ -366,7 +412,7 @@ module RIMS
366
412
  @logger.info("mail delivery user: #{username}")
367
413
  MailDeliveryDecoder.new(self, @drb_services, @auth, @logger)
368
414
  else
369
- engine = self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|msg| yield(msg) }
415
+ engine = self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|untagged_response| yield(untagged_response) }
370
416
  UserMailboxDecoder.new(self, engine, @auth, @logger)
371
417
  end
372
418
  end
@@ -377,137 +423,137 @@ module RIMS
377
423
  auth_reader = AuthenticationReader.new(@auth, client_response_input_gets, server_challenge_output_write, @logger)
378
424
  if (username = auth_reader.authenticate_client(auth_type, inline_client_response_data_base64)) then
379
425
  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"
426
+ @logger.info("authentication OK: #{username}")
427
+ @next_decoder = accept_authentication(username) {|untagged_response|
428
+ yield(untagged_response)
429
+ yield(:flush)
384
430
  }
431
+ yield("#{tag} OK AUTHENTICATE #{auth_type} success\r\n")
385
432
  else
386
433
  @logger.info('bad authentication.')
387
- yield([ "#{tag} BAD AUTHENTICATE failed\r\n" ])
434
+ yield("#{tag} BAD AUTHENTICATE failed\r\n")
388
435
  end
389
436
  else
390
- yield([ "#{tag} NO authentication failed\r\n" ])
437
+ yield("#{tag} NO authentication failed\r\n")
391
438
  end
392
439
  end
393
440
  imap_command :authenticate
394
441
 
395
442
  def login(tag, username, password)
396
443
  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"
444
+ @logger.info("login authentication OK: #{username}")
445
+ @next_decoder = accept_authentication(username) {|untagged_response|
446
+ yield(untagged_response)
447
+ yield(:flush)
401
448
  }
449
+ yield("#{tag} OK LOGIN completed\r\n")
402
450
  else
403
- yield([ "#{tag} NO failed to login\r\n" ])
451
+ yield("#{tag} NO failed to login\r\n")
404
452
  end
405
453
  end
406
454
  imap_command :login
407
455
 
408
- def select(tag, mbox_name)
409
- yield(make_not_authenticated_response(tag))
456
+ def select(tag, mbox_name, &block)
457
+ make_not_authenticated_response(tag, &block)
410
458
  end
411
459
  imap_command :select
412
460
 
413
- def examine(tag, mbox_name)
414
- yield(make_not_authenticated_response(tag))
461
+ def examine(tag, mbox_name, &block)
462
+ make_not_authenticated_response(tag, &block)
415
463
  end
416
464
  imap_command :examine
417
465
 
418
- def create(tag, mbox_name)
419
- yield(make_not_authenticated_response(tag))
466
+ def create(tag, mbox_name, &block)
467
+ make_not_authenticated_response(tag, &block)
420
468
  end
421
469
  imap_command :create
422
470
 
423
- def delete(tag, mbox_name)
424
- yield(make_not_authenticated_response(tag))
471
+ def delete(tag, mbox_name, &block)
472
+ make_not_authenticated_response(tag, &block)
425
473
  end
426
474
  imap_command :delete
427
475
 
428
- def rename(tag, src_name, dst_name)
429
- yield(make_not_authenticated_response(tag))
476
+ def rename(tag, src_name, dst_name, &block)
477
+ make_not_authenticated_response(tag, &block)
430
478
  end
431
479
  imap_command :rename
432
480
 
433
- def subscribe(tag, mbox_name)
434
- yield(make_not_authenticated_response(tag))
481
+ def subscribe(tag, mbox_name, &block)
482
+ make_not_authenticated_response(tag, &block)
435
483
  end
436
484
  imap_command :subscribe
437
485
 
438
- def unsubscribe(tag, mbox_name)
439
- yield(make_not_authenticated_response(tag))
486
+ def unsubscribe(tag, mbox_name, &block)
487
+ make_not_authenticated_response(tag, &block)
440
488
  end
441
489
  imap_command :unsubscribe
442
490
 
443
- def list(tag, ref_name, mbox_name)
444
- yield(make_not_authenticated_response(tag))
491
+ def list(tag, ref_name, mbox_name, &block)
492
+ make_not_authenticated_response(tag, &block)
445
493
  end
446
494
  imap_command :list
447
495
 
448
- def lsub(tag, ref_name, mbox_name)
449
- yield(make_not_authenticated_response(tag))
496
+ def lsub(tag, ref_name, mbox_name, &block)
497
+ make_not_authenticated_response(tag, &block)
450
498
  end
451
499
  imap_command :lsub
452
500
 
453
- def status(tag, mbox_name, data_item_group)
454
- yield(make_not_authenticated_response(tag))
501
+ def status(tag, mbox_name, data_item_group, &block)
502
+ make_not_authenticated_response(tag, &block)
455
503
  end
456
504
  imap_command :status
457
505
 
458
- def append(tag, mbox_name, *opt_args, msg_text)
459
- yield(make_not_authenticated_response(tag))
506
+ def append(tag, mbox_name, *opt_args, msg_text, &block)
507
+ make_not_authenticated_response(tag, &block)
460
508
  end
461
509
  imap_command :append
462
510
 
463
- def check(tag)
464
- yield(make_not_authenticated_response(tag))
511
+ def check(tag, &block)
512
+ make_not_authenticated_response(tag, &block)
465
513
  end
466
514
  imap_command :check
467
515
 
468
- def close(tag)
469
- yield(make_not_authenticated_response(tag))
516
+ def close(tag, &block)
517
+ make_not_authenticated_response(tag, &block)
470
518
  end
471
519
  imap_command :close
472
520
 
473
- def expunge(tag)
474
- yield(make_not_authenticated_response(tag))
521
+ def expunge(tag, &block)
522
+ make_not_authenticated_response(tag, &block)
475
523
  end
476
524
  imap_command :expunge
477
525
 
478
- def search(tag, *cond_args, uid: false)
479
- yield(make_not_authenticated_response(tag))
526
+ def search(tag, *cond_args, uid: false, &block)
527
+ make_not_authenticated_response(tag, &block)
480
528
  end
481
529
  imap_command :search
482
530
 
483
- def fetch(tag, msg_set, data_item_group, uid: false)
484
- yield(make_not_authenticated_response(tag))
531
+ def fetch(tag, msg_set, data_item_group, uid: false, &block)
532
+ make_not_authenticated_response(tag, &block)
485
533
  end
486
534
  imap_command :fetch
487
535
 
488
- def store(tag, msg_set, data_item_name, data_item_value, uid: false)
489
- yield(make_not_authenticated_response(tag))
536
+ def store(tag, msg_set, data_item_name, data_item_value, uid: false, &block)
537
+ make_not_authenticated_response(tag, &block)
490
538
  end
491
539
  imap_command :store
492
540
 
493
- def copy(tag, msg_set, mbox_name, uid: false)
494
- yield(make_not_authenticated_response(tag))
541
+ def copy(tag, msg_set, mbox_name, uid: false, &block)
542
+ make_not_authenticated_response(tag, &block)
495
543
  end
496
544
  imap_command :copy
497
545
 
498
- def idle(tag, client_input_gets, server_output_write, connection_timer)
499
- yield(make_not_authenticated_response(tag))
546
+ def idle(tag, client_input_gets, server_output_write, connection_timer, &block)
547
+ make_not_authenticated_response(tag, &block)
500
548
  end
501
549
  imap_command :idle
502
550
  end
503
551
 
504
552
  class LogoutDecoder < Decoder
505
- def initialize(parent_decoder)
553
+ def initialize(parent_decoder, logger)
554
+ super(nil, logger)
506
555
  @parent_decoder = parent_decoder
507
- end
508
-
509
- def next_decoder
510
- self
556
+ @logger.debug("RIMS::Protocol::LogoutDecoder#initialize at #{self}") if @logger.debug?
511
557
  end
512
558
 
513
559
  def auth?
@@ -519,6 +565,7 @@ module RIMS
519
565
  end
520
566
 
521
567
  def cleanup
568
+ @logger.debug("RIMS::Protocol::LogoutDecoder#cleanup at #{self}") if @logger.debug?
522
569
  unless (@parent_decoder.nil?) then
523
570
  @parent_decoder.cleanup
524
571
  @parent_decoder = nil
@@ -652,31 +699,81 @@ module RIMS
652
699
  class AuthenticatedDecoder < Decoder
653
700
  def authenticate(tag, client_response_input_gets, server_challenge_output_write,
654
701
  auth_type, inline_client_response_data_base64=nil, &block)
655
- yield([ "#{tag} NO duplicated authentication\r\n" ])
702
+ yield("#{tag} NO duplicated authentication\r\n")
656
703
  end
657
704
  imap_command :authenticate
658
705
 
659
706
  def login(tag, username, password, &block)
660
- yield([ "#{tag} NO duplicated login\r\n" ])
707
+ yield("#{tag} NO duplicated login\r\n")
661
708
  end
662
709
  imap_command :login
663
710
  end
664
711
 
665
712
  class UserMailboxDecoder < AuthenticatedDecoder
713
+ class BulkResponse
714
+ def initialize(limit_count, limit_size)
715
+ @limit_count = limit_count
716
+ @limit_size = limit_size
717
+ @responses = []
718
+ @size = 0
719
+ end
720
+
721
+ def count
722
+ @responses.length
723
+ end
724
+
725
+ attr_reader :size
726
+
727
+ def add(response)
728
+ @responses << response
729
+ @size += response.bytesize
730
+ self
731
+ end
732
+
733
+ alias << add
734
+
735
+ def empty?
736
+ @responses.empty?
737
+ end
738
+
739
+ def full?
740
+ count >= @limit_count || size >= @limit_size
741
+ end
742
+
743
+ def flush
744
+ res = @responses
745
+ if (count >= @limit_count) then
746
+ res = [ res.join('') ]
747
+ end
748
+
749
+ @responses = []
750
+ @size = 0
751
+
752
+ res
753
+ end
754
+ end
755
+
666
756
  class Engine
667
757
  def initialize(unique_user_id, mail_store, logger,
668
758
  bulk_response_count: 100,
759
+ bulk_response_size: 1024**2 * 10,
669
760
  read_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
670
761
  write_lock_timeout_seconds: ReadWriteLock::DEFAULT_TIMEOUT_SECONDS,
671
- cleanup_write_lock_timeout_seconds: 1)
762
+ cleanup_write_lock_timeout_seconds: 1,
763
+ charset_aliases: RFC822::DEFAULT_CHARSET_ALIASES,
764
+ charset_convert_options: nil)
672
765
  @unique_user_id = unique_user_id
673
766
  @mail_store = mail_store
674
767
  @logger = logger
675
768
  @bulk_response_count = bulk_response_count
769
+ @bulk_response_size = bulk_response_size
676
770
  @read_lock_timeout_seconds = read_lock_timeout_seconds
677
771
  @write_lock_timeout_seconds = write_lock_timeout_seconds
678
772
  @cleanup_write_lock_timeout_seconds = cleanup_write_lock_timeout_seconds
773
+ @charset_aliases = charset_aliases
774
+ @charset_convert_options = charset_convert_options
679
775
  @folders = {}
776
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#initialize at #{self}") if @logger.debug?
680
777
  end
681
778
 
682
779
  attr_reader :unique_user_id
@@ -704,11 +801,13 @@ module RIMS
704
801
  end
705
802
  @folders[token] = folder
706
803
 
804
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#open_folder: #{token}") if @logger.debug?
707
805
  token
708
806
  end
709
807
  private :open_folder
710
808
 
711
809
  def close_folder(token)
810
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#close_folder: #{token}") if @logger.debug?
712
811
  folder = @folders.delete(token) or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
713
812
  folder.reload if folder.updated?
714
813
  begin
@@ -752,9 +851,10 @@ module RIMS
752
851
  end
753
852
 
754
853
  def destroy
854
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder::Engine#destroy at #{self}") if @logger.debug?
755
855
  tmp_mail_store = @mail_store
756
856
  ReadWriteLock.write_lock_timeout_detach(@cleanup_write_lock_timeout_seconds, @write_lock_timeout_seconds, logger: @logger) {|timeout_seconds|
757
- @mail_store.write_synchronize(timeout_seconds) {
857
+ tmp_mail_store.write_synchronize(timeout_seconds) {
758
858
  @logger.info("close mail store: #{@unique_user_id}")
759
859
  tmp_mail_store.close
760
860
  }
@@ -804,40 +904,66 @@ module RIMS
804
904
 
805
905
  class << self
806
906
  def imap_command_authenticated(name, **guard_optional)
907
+ name = name.to_sym
807
908
  orig_name = "_#{name}".to_sym
808
909
  alias_method orig_name, name
809
- define_method name, lambda{|token, tag, *args, **kw_args, &block|
810
- guard_authenticated(orig_name, token, tag, *args, **kw_args, **guard_optional, &block)
811
- }
812
- name.to_sym
910
+
911
+ guard_options_name = "_#{name}_guard_options".to_sym
912
+ define_method guard_options_name, lambda{ guard_optional }
913
+ private guard_options_name
914
+
915
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
916
+ def #{name}(token, tag, *args, **kw_args, &block)
917
+ guard_authenticated(:#{orig_name}, token, tag, *args, **kw_args, **#{guard_options_name}, &block)
918
+ end
919
+ EOF
920
+
921
+ name
813
922
  end
814
923
  private :imap_command_authenticated
815
924
 
816
925
  def imap_command_selected(name, **guard_optional)
926
+ name = name.to_sym
817
927
  orig_name = "_#{name}".to_sym
818
928
  alias_method orig_name, name
819
- define_method name, lambda{|token, tag, *args, **kw_args, &block|
820
- guard_selected(orig_name, token, tag, *args, **kw_args, **guard_optional, &block)
821
- }
822
- name.to_sym
929
+
930
+ guard_options_name = "_#{name}_guard_options".to_sym
931
+ define_method guard_options_name, lambda{ guard_optional }
932
+ private guard_options_name
933
+
934
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
935
+ def #{name}(token, tag, *args, **kw_args, &block)
936
+ guard_selected(:#{orig_name}, token, tag, *args, **kw_args, **#{guard_options_name}, &block)
937
+ end
938
+ EOF
939
+
940
+ name
823
941
  end
824
942
  private :imap_command_selected
825
943
  end
826
944
 
945
+ def new_bulk_response
946
+ BulkResponse.new(@bulk_response_count, @bulk_response_size)
947
+ end
948
+ private :new_bulk_response
949
+
827
950
  def noop(token, tag)
828
- res = []
951
+ res = new_bulk_response
829
952
  if (token) then
830
953
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
831
954
  begin
832
955
  @mail_store.read_synchronize(@read_lock_timeout_seconds) {
833
- folder.server_response_fetch{|r| res << r } if folder.server_response?
956
+ folder.server_response_fetch{|untagged_response|
957
+ res << untagged_response
958
+ yield(res.flush) if res.full?
959
+ }
834
960
  }
835
961
  rescue ReadLockTimeoutError
836
962
  @logger.warn("give up to get folder status because of read-lock timeout over #{@read_lock_timeout_seconds} seconds")
837
963
  end
838
964
  end
839
965
  res << "#{tag} OK NOOP completed\r\n"
840
- yield(res)
966
+ yield(res.flush)
841
967
  end
842
968
 
843
969
  def folder_open_msgs(token)
@@ -859,20 +985,21 @@ module RIMS
859
985
  close_no_response(token)
860
986
  end
861
987
 
862
- res = []
988
+ res = new_bulk_response
863
989
  new_token = nil
864
990
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
865
991
 
866
992
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
867
993
  new_token = open_folder(id)
868
- folder_open_msgs(new_token) do |msg|
869
- res << msg
870
- end
994
+ folder_open_msgs(new_token) {|untagged_response|
995
+ res << untagged_response
996
+ yield(res.flush) if res.full?
997
+ }
871
998
  res << "#{tag} OK [READ-WRITE] SELECT completed\r\n"
872
999
  else
873
1000
  res << "#{tag} NO not found a mailbox\r\n"
874
1001
  end
875
- yield(res)
1002
+ yield(res.flush)
876
1003
 
877
1004
  new_token
878
1005
  end
@@ -883,30 +1010,34 @@ module RIMS
883
1010
  close_no_response(token)
884
1011
  end
885
1012
 
886
- res = []
1013
+ res = new_bulk_response
887
1014
  new_token = nil
888
1015
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
889
1016
 
890
1017
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
891
1018
  new_token = open_folder(id, read_only: true)
892
- folder_open_msgs(new_token) do |msg|
893
- res << msg
894
- end
1019
+ folder_open_msgs(new_token) {|untagged_response|
1020
+ res << untagged_response
1021
+ yield(res.flush) if res.full?
1022
+ }
895
1023
  res << "#{tag} OK [READ-ONLY] EXAMINE completed\r\n"
896
1024
  else
897
1025
  res << "#{tag} NO not found a mailbox\r\n"
898
1026
  end
899
- yield(res)
1027
+ yield(res.flush)
900
1028
 
901
1029
  new_token
902
1030
  end
903
1031
  imap_command_authenticated :examine
904
1032
 
905
1033
  def create(token, tag, mbox_name)
906
- res = []
1034
+ res = new_bulk_response
907
1035
  if (token) then
908
1036
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
909
- folder.server_response_fetch{|r| res << r }
1037
+ folder.server_response_fetch{|untagged_response|
1038
+ res << untagged_response
1039
+ yield(res.flush) if res.full?
1040
+ }
910
1041
  end
911
1042
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
912
1043
  if (@mail_store.mbox_id(mbox_name_utf8)) then
@@ -915,15 +1046,18 @@ module RIMS
915
1046
  @mail_store.add_mbox(mbox_name_utf8)
916
1047
  res << "#{tag} OK CREATE completed\r\n"
917
1048
  end
918
- yield(res)
1049
+ yield(res.flush)
919
1050
  end
920
1051
  imap_command_authenticated :create, exclusive: true
921
1052
 
922
1053
  def delete(token, tag, mbox_name)
923
- res = []
1054
+ res = new_bulk_response
924
1055
  if (token) then
925
1056
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
926
- folder.server_response_fetch{|r| res << r }
1057
+ folder.server_response_fetch{|untagged_response|
1058
+ res << untagged_response
1059
+ yield(res.flush) if res.full?
1060
+ }
927
1061
  end
928
1062
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
929
1063
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
@@ -936,38 +1070,47 @@ module RIMS
936
1070
  else
937
1071
  res << "#{tag} NO not found a mailbox\r\n"
938
1072
  end
939
- yield(res)
1073
+ yield(res.flush)
940
1074
  end
941
1075
  imap_command_authenticated :delete, exclusive: true
942
1076
 
943
1077
  def rename(token, tag, src_name, dst_name)
944
- res = []
1078
+ res = new_bulk_response
945
1079
  if (token) then
946
1080
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
947
- folder.server_response_fetch{|r| res << r }
1081
+ folder.server_response_fetch{|untagged_response|
1082
+ res << untagged_response
1083
+ yield(res.flush) if res.full?
1084
+ }
948
1085
  end
949
1086
  src_name_utf8 = Net::IMAP.decode_utf7(src_name)
950
1087
  dst_name_utf8 = Net::IMAP.decode_utf7(dst_name)
951
1088
  unless (id = @mail_store.mbox_id(src_name_utf8)) then
952
- return yield(res << "#{tag} NO not found a mailbox\r\n")
1089
+ res << "#{tag} NO not found a mailbox\r\n"
1090
+ return yield(res.flush)
953
1091
  end
954
1092
  if (id == @mail_store.mbox_id('INBOX')) then
955
- return yield(res << "#{tag} NO not rename inbox\r\n")
1093
+ res << "#{tag} NO not rename inbox\r\n"
1094
+ return yield(res.flush)
956
1095
  end
957
1096
  if (@mail_store.mbox_id(dst_name_utf8)) then
958
- return yield(res << "#{tag} NO duplicated mailbox\r\n")
1097
+ res << "#{tag} NO duplicated mailbox\r\n"
1098
+ return yield(res.flush)
959
1099
  end
960
1100
  @mail_store.rename_mbox(id, dst_name_utf8)
961
1101
  res << "#{tag} OK RENAME completed\r\n"
962
- yield(res)
1102
+ yield(res.flush)
963
1103
  end
964
1104
  imap_command_authenticated :rename, exclusive: true
965
1105
 
966
1106
  def subscribe(token, tag, mbox_name)
967
- res = []
1107
+ res = new_bulk_response
968
1108
  if (token) then
969
1109
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
970
- folder.server_response_fetch{|r| res << r }
1110
+ folder.server_response_fetch{|untagged_response|
1111
+ res << untagged_response
1112
+ yield(res.flush) if res.full?
1113
+ }
971
1114
  end
972
1115
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
973
1116
  if (@mail_store.mbox_id(mbox_name_utf8)) then
@@ -975,15 +1118,18 @@ module RIMS
975
1118
  else
976
1119
  res << "#{tag} NO not found a mailbox\r\n"
977
1120
  end
978
- yield(res)
1121
+ yield(res.flush)
979
1122
  end
980
1123
  imap_command_authenticated :subscribe
981
1124
 
982
1125
  def unsubscribe(token, tag, mbox_name)
983
- res = []
1126
+ res = new_bulk_response
984
1127
  if (token) then
985
1128
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
986
- folder.server_response_fetch{|r| res << r }
1129
+ folder.server_response_fetch{|untagged_response|
1130
+ res << untagged_response
1131
+ yield(res.flush) if res.full?
1132
+ }
987
1133
  end
988
1134
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
989
1135
  if (@mail_store.mbox_id(mbox_name_utf8)) then
@@ -991,7 +1137,7 @@ module RIMS
991
1137
  else
992
1138
  res << "#{tag} NO not found a mailbox\r\n"
993
1139
  end
994
- yield(res)
1140
+ yield(res.flush)
995
1141
  end
996
1142
  imap_command_authenticated :unsubscribe
997
1143
 
@@ -1020,46 +1166,57 @@ module RIMS
1020
1166
  private :list_mbox
1021
1167
 
1022
1168
  def list(token, tag, ref_name, mbox_name)
1023
- res = []
1169
+ res = new_bulk_response
1024
1170
  if (token) then
1025
1171
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1026
- folder.server_response_fetch{|r| res << r }
1172
+ folder.server_response_fetch{|untagged_response|
1173
+ res << untagged_response
1174
+ yield(res.flush) if res.full?
1175
+ }
1027
1176
  end
1028
1177
  if (mbox_name.empty?) then
1029
1178
  res << "* LIST (\\Noselect) NIL \"\"\r\n"
1030
1179
  else
1031
- list_mbox(ref_name, mbox_name) do |mbox_entry|
1180
+ list_mbox(ref_name, mbox_name) {|mbox_entry|
1032
1181
  res << "* LIST #{mbox_entry}\r\n"
1033
- end
1182
+ yield(res.flush) if res.full?
1183
+ }
1034
1184
  end
1035
1185
  res << "#{tag} OK LIST completed\r\n"
1036
- yield(res)
1186
+ yield(res.flush)
1037
1187
  end
1038
1188
  imap_command_authenticated :list
1039
1189
 
1040
1190
  def lsub(token, tag, ref_name, mbox_name)
1041
- res = []
1191
+ res = new_bulk_response
1042
1192
  if (token) then
1043
1193
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1044
- folder.server_response_fetch{|r| res << r }
1194
+ folder.server_response_fetch{|untagged_response|
1195
+ res << untagged_response
1196
+ yield(res.flush) if res.full?
1197
+ }
1045
1198
  end
1046
1199
  if (mbox_name.empty?) then
1047
1200
  res << "* LSUB (\\Noselect) NIL \"\"\r\n"
1048
1201
  else
1049
- list_mbox(ref_name, mbox_name) do |mbox_entry|
1202
+ list_mbox(ref_name, mbox_name) {|mbox_entry|
1050
1203
  res << "* LSUB #{mbox_entry}\r\n"
1051
- end
1204
+ yield(res.flush) if res.full?
1205
+ }
1052
1206
  end
1053
1207
  res << "#{tag} OK LSUB completed\r\n"
1054
- yield(res)
1208
+ yield(res.flush)
1055
1209
  end
1056
1210
  imap_command_authenticated :lsub
1057
1211
 
1058
1212
  def status(token, tag, mbox_name, data_item_group)
1059
- res = []
1213
+ res = new_bulk_response
1060
1214
  if (token) then
1061
1215
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1062
- folder.server_response_fetch{|r| res << r }
1216
+ folder.server_response_fetch{|untagged_response|
1217
+ res << untagged_response
1218
+ yield(res.flush) if res.full?
1219
+ }
1063
1220
  end
1064
1221
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
1065
1222
  if (id = @mail_store.mbox_id(mbox_name_utf8)) then
@@ -1091,7 +1248,7 @@ module RIMS
1091
1248
  else
1092
1249
  res << "#{tag} NO not found a mailbox\r\n"
1093
1250
  end
1094
- yield(res)
1251
+ yield(res.flush)
1095
1252
  end
1096
1253
  imap_command_authenticated :status
1097
1254
 
@@ -1112,7 +1269,7 @@ module RIMS
1112
1269
  private :mailbox_size_server_response_multicast_push
1113
1270
 
1114
1271
  def append(token, tag, mbox_name, *opt_args, msg_text)
1115
- res = []
1272
+ res = new_bulk_response
1116
1273
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
1117
1274
  if (mbox_id = @mail_store.mbox_id(mbox_name_utf8)) then
1118
1275
  msg_flags = []
@@ -1161,30 +1318,39 @@ module RIMS
1161
1318
  mailbox_size_server_response_multicast_push(mbox_id)
1162
1319
  if (token) then
1163
1320
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1164
- folder.server_response_fetch{|r| res << r }
1321
+ folder.server_response_fetch{|untagged_response|
1322
+ res << untagged_response
1323
+ yield(res.flush) if res.full?
1324
+ }
1165
1325
  end
1166
1326
 
1167
1327
  res << "#{tag} OK [APPENDUID #{mbox_id} #{uid}] APPEND completed\r\n"
1168
1328
  else
1169
1329
  if (token) then
1170
1330
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1171
- folder.server_response_fetch{|r| res << r }
1331
+ folder.server_response_fetch{|untagged_response|
1332
+ res << untagged_response
1333
+ yield(res.flush) if res.full?
1334
+ }
1172
1335
  end
1173
1336
  res << "#{tag} NO [TRYCREATE] not found a mailbox\r\n"
1174
1337
  end
1175
- yield(res)
1338
+ yield(res.flush)
1176
1339
  end
1177
1340
  imap_command_authenticated :append, exclusive: true
1178
1341
 
1179
1342
  def check(token, tag)
1180
- res = []
1343
+ res = new_bulk_response
1181
1344
  if (token) then
1182
1345
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1183
- folder.server_response_fetch{|r| res << r }
1346
+ folder.server_response_fetch{|untagged_response|
1347
+ res << untagged_response
1348
+ yield(res.flush) if res.full?
1349
+ }
1184
1350
  end
1185
1351
  @mail_store.sync
1186
1352
  res << "#{tag} OK CHECK completed\r\n"
1187
- yield(res)
1353
+ yield(res.flush)
1188
1354
  end
1189
1355
  imap_command_selected :check, exclusive: true
1190
1356
 
@@ -1212,27 +1378,21 @@ module RIMS
1212
1378
  return yield([ "#{tag} NO cannot expunge in read-only mode\r\n" ]) if folder.read_only?
1213
1379
  folder.reload if folder.updated?
1214
1380
 
1215
- res = []
1216
- folder.server_response_fetch{|r|
1217
- res << r
1218
- if (res.length >= @bulk_response_count) then
1219
- yield(res)
1220
- res = []
1221
- end
1222
- }
1381
+ res = new_bulk_response
1382
+ folder.server_response_fetch{|untagged_response|
1383
+ res << untagged_response
1384
+ yield(res.flush) if res.full?
1385
+ }
1223
1386
 
1224
1387
  folder.expunge_mbox do |msg_num|
1225
- r = "* #{msg_num} EXPUNGE\r\n"
1226
- res << r
1227
- if (res.length >= @bulk_response_count) then
1228
- yield(res)
1229
- res = []
1230
- end
1231
- folder.server_response_multicast_push(r)
1388
+ untagged_response = "* #{msg_num} EXPUNGE\r\n"
1389
+ res << untagged_response
1390
+ yield(res.flush) if res.full?
1391
+ folder.server_response_multicast_push(untagged_response)
1232
1392
  end
1233
1393
 
1234
1394
  res << "#{tag} OK EXPUNGE completed\r\n"
1235
- yield(res)
1395
+ yield(res.flush)
1236
1396
  end
1237
1397
  imap_command_selected :expunge, exclusive: true
1238
1398
 
@@ -1240,7 +1400,9 @@ module RIMS
1240
1400
  folder = @folders[token] or raise KeyError.new("undefined folder token: #{token}", key: token, receiver: self)
1241
1401
  folder.should_be_alive
1242
1402
  folder.reload if folder.updated?
1243
- parser = SearchParser.new(@mail_store, folder)
1403
+ parser = SearchParser.new(@mail_store, folder,
1404
+ charset_aliases: @charset_aliases,
1405
+ charset_convert_options: @charset_convert_options)
1244
1406
 
1245
1407
  if (! cond_args.empty? && cond_args[0].upcase == 'CHARSET') then
1246
1408
  cond_args.shift
@@ -1250,7 +1412,7 @@ module RIMS
1250
1412
  parser.charset = charset_string
1251
1413
  rescue ArgumentError
1252
1414
  @logger.warn("unknown charset: #{charset_string}")
1253
- return yield([ "#{tag} NO [BADCHARSET (#{Encoding.list.map(&:to_s).join(' ')})] unknown charset\r\n" ])
1415
+ return yield([ "#{tag} NO [BADCHARSET (#{Encoding.list.reject(&:dummy?).map(&:to_s).join(' ')})] unknown charset\r\n" ])
1254
1416
  end
1255
1417
  end
1256
1418
 
@@ -1261,35 +1423,32 @@ module RIMS
1261
1423
  if (cond_args[0].upcase == 'UID' && cond_args.length >= 2) then
1262
1424
  begin
1263
1425
  msg_set = folder.parse_msg_set(cond_args[1], uid: true)
1264
- msg_src = folder.msg_find_all(msg_set, uid: true)
1426
+ msg_list = folder.msg_find_all(msg_set, uid: true)
1265
1427
  cond_args.shift(2)
1266
1428
  rescue MessageSetSyntaxError
1267
- msg_src = folder.each_msg
1429
+ msg_list = folder.each_msg
1268
1430
  end
1269
1431
  else
1270
1432
  begin
1271
1433
  msg_set = folder.parse_msg_set(cond_args[0], uid: false)
1272
- msg_src = folder.msg_find_all(msg_set, uid: false)
1434
+ msg_list = folder.msg_find_all(msg_set, uid: false)
1273
1435
  cond_args.shift
1274
1436
  rescue MessageSetSyntaxError
1275
- msg_src = folder.each_msg
1437
+ msg_list = folder.each_msg
1276
1438
  end
1277
1439
  end
1278
1440
  cond = parser.parse(cond_args)
1279
1441
 
1280
- res = []
1281
- folder.server_response_fetch{|r|
1282
- res << r
1283
- if (res.length >= @bulk_response_count) then
1284
- yield(res)
1285
- res = []
1286
- end
1442
+ res = new_bulk_response
1443
+ folder.server_response_fetch{|untagged_response|
1444
+ res << untagged_response
1445
+ yield(res.flush) if res.full?
1287
1446
  }
1288
1447
 
1289
1448
  res << '* SEARCH'
1290
1449
  begin
1291
1450
  begin
1292
- for msg in msg_src
1451
+ for msg in msg_list
1293
1452
  begin
1294
1453
  if (cond.call(msg)) then
1295
1454
  if (uid) then
@@ -1297,10 +1456,7 @@ module RIMS
1297
1456
  else
1298
1457
  res << " #{msg.num}"
1299
1458
  end
1300
- if (res.length >= @bulk_response_count) then
1301
- yield(res)
1302
- res = []
1303
- end
1459
+ yield(res.flush) if res.full?
1304
1460
  end
1305
1461
  rescue EncodingError
1306
1462
  @logger.warn("encoding error at the message: uidvalidity(#{folder.mbox_id}) uid(#{msg.uid})")
@@ -1311,14 +1467,12 @@ module RIMS
1311
1467
  res << "\r\n"
1312
1468
  end
1313
1469
  rescue
1314
- # flush bulk response
1315
- yield(res)
1316
- res = []
1470
+ yield(res.flush)
1317
1471
  raise
1318
1472
  end
1319
1473
 
1320
1474
  res << "#{tag} OK SEARCH completed\r\n"
1321
- yield(res)
1475
+ yield(res.flush)
1322
1476
  end
1323
1477
  imap_command_selected :search
1324
1478
 
@@ -1339,28 +1493,22 @@ module RIMS
1339
1493
  end
1340
1494
  end
1341
1495
 
1342
- parser = FetchParser.new(@mail_store, folder)
1496
+ parser = FetchParser.new(@mail_store, folder, charset_aliases: @charset_aliases)
1343
1497
  fetch = parser.parse(data_item_group)
1344
1498
 
1345
- res = []
1346
- folder.server_response_fetch{|r|
1347
- res << r
1348
- if (res.length >= @bulk_response_count) then
1349
- yield(res)
1350
- res = []
1351
- end
1499
+ res = new_bulk_response
1500
+ folder.server_response_fetch{|untagged_response|
1501
+ res << untagged_response
1502
+ yield(res.flush) if res.full?
1352
1503
  }
1353
1504
 
1354
1505
  for msg in msg_list
1355
1506
  res << ('* '.b << msg.num.to_s.b << ' FETCH '.b << fetch.call(msg) << "\r\n".b)
1356
- if (res.length >= @bulk_response_count) then
1357
- yield(res)
1358
- res = []
1359
- end
1507
+ yield(res.flush) if res.full?
1360
1508
  end
1361
1509
 
1362
1510
  res << "#{tag} OK FETCH completed\r\n"
1363
- yield(res)
1511
+ yield(res.flush)
1364
1512
  end
1365
1513
  imap_command_selected :fetch
1366
1514
 
@@ -1440,18 +1588,15 @@ module RIMS
1440
1588
  end
1441
1589
  end
1442
1590
 
1443
- res = []
1444
- folder.server_response_fetch{|r|
1445
- res << r
1446
- if (res.length >= @bulk_response_count) then
1447
- yield(res)
1448
- res = []
1449
- end
1591
+ res = new_bulk_response
1592
+ folder.server_response_fetch{|untagged_response|
1593
+ res << untagged_response
1594
+ yield(res.flush) if res.full?
1450
1595
  }
1451
1596
 
1452
1597
  if (is_silent) then
1453
1598
  res << "#{tag} OK STORE completed\r\n"
1454
- yield(res)
1599
+ yield(res.flush)
1455
1600
  else
1456
1601
  for msg in msg_list
1457
1602
  flag_atom_list = nil
@@ -1471,17 +1616,14 @@ module RIMS
1471
1616
  else
1472
1617
  res << "* #{msg.num} FETCH (FLAGS (#{flag_atom_list.join(' ')}))\r\n"
1473
1618
  end
1474
- if (res.length >= @bulk_response_count) then
1475
- yield(res)
1476
- res = []
1477
- end
1619
+ yield(res.flush) if res.full?
1478
1620
  else
1479
1621
  @logger.warn("not found a message and skipped: uidvalidity(#{folder.mbox_id}) uid(#{msg.uid})")
1480
1622
  end
1481
1623
  end
1482
1624
 
1483
1625
  res << "#{tag} OK STORE completed\r\n"
1484
- yield(res)
1626
+ yield(res.flush)
1485
1627
  end
1486
1628
  end
1487
1629
  imap_command_selected :store, exclusive: true
@@ -1491,7 +1633,7 @@ module RIMS
1491
1633
  folder.should_be_alive
1492
1634
  folder.reload if folder.updated?
1493
1635
 
1494
- res = []
1636
+ res = new_bulk_response
1495
1637
  mbox_name_utf8 = Net::IMAP.decode_utf7(mbox_name)
1496
1638
  msg_set = folder.parse_msg_set(msg_set, uid: uid)
1497
1639
 
@@ -1507,17 +1649,26 @@ module RIMS
1507
1649
 
1508
1650
  if (msg_list.size > 0) then
1509
1651
  mailbox_size_server_response_multicast_push(mbox_id)
1510
- folder.server_response_fetch{|r| res << r }
1652
+ folder.server_response_fetch{|untagged_response|
1653
+ res << untagged_response
1654
+ yield(res.flush) if res.full?
1655
+ }
1511
1656
  res << "#{tag} OK [COPYUID #{mbox_id} #{src_uids.join(',')} #{dst_uids.join(',')}] COPY completed\r\n"
1512
1657
  else
1513
- folder.server_response_fetch{|r| res << r }
1658
+ folder.server_response_fetch{|untagged_response|
1659
+ res << untagged_response
1660
+ yield(res.flush) if res.full?
1661
+ }
1514
1662
  res << "#{tag} OK COPY completed\r\n"
1515
1663
  end
1516
1664
  else
1517
- folder.server_response_fetch{|r| res << r }
1665
+ folder.server_response_fetch{|untagged_response|
1666
+ res << untagged_response
1667
+ yield(res.flush) if res.full?
1668
+ }
1518
1669
  res << "#{tag} NO [TRYCREATE] not found a mailbox\r\n"
1519
1670
  end
1520
- yield(res)
1671
+ yield(res.flush)
1521
1672
  end
1522
1673
  imap_command_selected :copy, exclusive: true
1523
1674
 
@@ -1529,12 +1680,15 @@ module RIMS
1529
1680
  server_output_write.call([ "+ continue\r\n" ])
1530
1681
 
1531
1682
  server_response_thread = Thread.new{
1532
- @logger.info('idle server response thread start... ')
1683
+ res = new_bulk_response
1684
+ @logger.info('idle server response thread start...')
1533
1685
  folder.server_response_idle_wait{|server_response_list|
1534
- for server_response in server_response_list
1535
- @logger.debug("idle server response: #{server_response}") if @logger.debug?
1686
+ for untagged_response in server_response_list
1687
+ @logger.debug("idle server response: #{untagged_response}") if @logger.debug?
1688
+ res << untagged_response
1689
+ server_output_write.call(res.flush) if res.full?
1536
1690
  end
1537
- server_output_write.call(server_response_list)
1691
+ server_output_write.call(res.flush) unless res.empty?
1538
1692
  }
1539
1693
  @logger.info('idle server response thread terminated.')
1540
1694
  }
@@ -1547,23 +1701,23 @@ module RIMS
1547
1701
  server_response_thread.join
1548
1702
  end
1549
1703
 
1550
- res = []
1704
+ last_res = []
1551
1705
  if (line) then
1552
1706
  line.chomp!("\n")
1553
1707
  line.chomp!("\r")
1554
1708
  if (line.upcase == "DONE") then
1555
1709
  @logger.info('idle terminated.')
1556
- res << "#{tag} OK IDLE terminated\r\n"
1710
+ last_res << "#{tag} OK IDLE terminated\r\n"
1557
1711
  else
1558
1712
  @logger.warn('unexpected client response and idle terminated.')
1559
1713
  @logger.debug("unexpected client response data: #{line}") if @logger.debug?
1560
- res << "#{tag} BAD unexpected client response\r\n"
1714
+ last_res << "#{tag} BAD unexpected client response\r\n"
1561
1715
  end
1562
1716
  else
1563
1717
  @logger.warn('unexpected client connection close and idle terminated.')
1564
- res << "#{tag} BAD unexpected client connection close\r\n"
1718
+ last_res << "#{tag} BAD unexpected client connection close\r\n"
1565
1719
  end
1566
- yield(res)
1720
+ yield(last_res)
1567
1721
  end
1568
1722
  imap_command_selected :idle, exclusive: nil
1569
1723
  end
@@ -1573,6 +1727,7 @@ module RIMS
1573
1727
  @parent_decoder = parent_decoder
1574
1728
  @engine = engine
1575
1729
  @token = nil
1730
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder#initialize at #{self}") if @logger.debug?
1576
1731
  end
1577
1732
 
1578
1733
  def auth?
@@ -1583,43 +1738,57 @@ module RIMS
1583
1738
  ! @token.nil?
1584
1739
  end
1585
1740
 
1586
- def cleanup
1741
+ # `not_cleanup_parent' keyword argument is defined for MailDeliveryDecoder
1742
+ def cleanup(not_cleanup_parent: false)
1743
+ @logger.debug("RIMS::Protocol::UserMailboxDecoder#cleanup at #{self}") if @logger.debug?
1744
+
1587
1745
  unless (@engine.nil?) then
1588
1746
  begin
1589
1747
  @engine.cleanup(@token)
1590
1748
  ensure
1591
1749
  @token = nil
1592
1750
  end
1751
+ end
1593
1752
 
1594
- begin
1595
- @engine.destroy
1596
- ensure
1597
- @engine = nil
1753
+ unless (not_cleanup_parent) then
1754
+ unless (@engine.nil?) then
1755
+ begin
1756
+ @engine.destroy
1757
+ ensure
1758
+ @engine = nil
1759
+ end
1598
1760
  end
1599
- end
1600
1761
 
1601
- unless (@parent_decoder.nil?) then
1602
- @parent_decoder.cleanup
1603
- @parent_decoder = nil
1762
+ unless (@parent_decoder.nil?) then
1763
+ @parent_decoder.cleanup
1764
+ @parent_decoder = nil
1765
+ end
1604
1766
  end
1605
1767
 
1606
1768
  nil
1607
1769
  end
1608
1770
 
1609
- def noop(tag, &block)
1610
- @engine.noop(@token, tag, &block)
1771
+ def noop(tag)
1772
+ ret_val = nil
1773
+ @engine.noop(@token, tag) {|res|
1774
+ for response in res
1775
+ ret_val = yield(response)
1776
+ end
1777
+ }
1778
+
1779
+ ret_val
1611
1780
  end
1612
1781
  imap_command :noop
1613
1782
 
1614
- def logout(tag)
1783
+ def logout(tag, &block)
1615
1784
  if (@token) then
1616
1785
  old_token = @token
1617
1786
  @token = nil
1618
1787
  @engine.cleanup(old_token)
1619
1788
  end
1620
1789
 
1621
- @next_decoder = LogoutDecoder.new(self)
1622
- yield(make_logout_response(tag))
1790
+ @next_decoder = LogoutDecoder.new(self, @logger)
1791
+ make_logout_response(tag, &block)
1623
1792
  end
1624
1793
  imap_command :logout
1625
1794
 
@@ -1627,7 +1796,9 @@ module RIMS
1627
1796
  ret_val = nil
1628
1797
  old_token = @token
1629
1798
  @token = @engine.select(old_token, tag, mbox_name) {|res|
1630
- ret_val = yield(res)
1799
+ for response in res
1800
+ ret_val = yield(response)
1801
+ end
1631
1802
  }
1632
1803
 
1633
1804
  ret_val
@@ -1638,134 +1809,225 @@ module RIMS
1638
1809
  ret_val = nil
1639
1810
  old_token = @token
1640
1811
  @token = @engine.examine(old_token, tag, mbox_name) {|res|
1641
- ret_val = yield(res)
1812
+ for response in res
1813
+ ret_val = yield(response)
1814
+ end
1642
1815
  }
1643
1816
 
1644
1817
  ret_val
1645
1818
  end
1646
1819
  imap_command :examine
1647
1820
 
1648
- def create(tag, mbox_name, &block)
1649
- @engine.create(@token, tag, mbox_name, &block)
1821
+ def create(tag, mbox_name)
1822
+ ret_val = nil
1823
+ @engine.create(@token, tag, mbox_name) {|res|
1824
+ for response in res
1825
+ ret_val = yield(response)
1826
+ end
1827
+ }
1828
+
1829
+ ret_val
1650
1830
  end
1651
1831
  imap_command :create
1652
1832
 
1653
- def delete(tag, mbox_name, &block)
1654
- @engine.delete(@token, tag, mbox_name, &block)
1833
+ def delete(tag, mbox_name)
1834
+ ret_val = nil
1835
+ @engine.delete(@token, tag, mbox_name) {|res|
1836
+ for response in res
1837
+ ret_val = yield(response)
1838
+ end
1839
+ }
1840
+
1841
+ ret_val
1655
1842
  end
1656
1843
  imap_command :delete
1657
1844
 
1658
- def rename(tag, src_name, dst_name, &block)
1659
- @engine.rename(@token, tag, src_name, dst_name, &block)
1845
+ def rename(tag, src_name, dst_name)
1846
+ ret_val = nil
1847
+ @engine.rename(@token, tag, src_name, dst_name) {|res|
1848
+ for response in res
1849
+ ret_val = yield(response)
1850
+ end
1851
+ }
1852
+
1853
+ ret_val
1660
1854
  end
1661
1855
  imap_command :rename
1662
1856
 
1663
- def subscribe(tag, mbox_name, &block)
1664
- @engine.subscribe(@token, tag, mbox_name, &block)
1857
+ def subscribe(tag, mbox_name)
1858
+ ret_val = nil
1859
+ @engine.subscribe(@token, tag, mbox_name) {|res|
1860
+ for response in res
1861
+ ret_val = yield(response)
1862
+ end
1863
+ }
1864
+
1865
+ ret_val
1665
1866
  end
1666
1867
  imap_command :subscribe
1667
1868
 
1668
- def unsubscribe(tag, mbox_name, &block)
1669
- @engine.unsubscribe(@token, tag, mbox_name, &block)
1869
+ def unsubscribe(tag, mbox_name)
1870
+ ret_val = nil
1871
+ @engine.unsubscribe(@token, tag, mbox_name) {|res|
1872
+ for response in res
1873
+ ret_val = yield(response)
1874
+ end
1875
+ }
1876
+
1877
+ ret_val
1670
1878
  end
1671
1879
  imap_command :unsubscribe
1672
1880
 
1673
- def list(tag, ref_name, mbox_name, &block)
1674
- @engine.list(@token, tag, ref_name, mbox_name, &block)
1881
+ def list(tag, ref_name, mbox_name)
1882
+ ret_val = nil
1883
+ @engine.list(@token, tag, ref_name, mbox_name) {|res|
1884
+ for response in res
1885
+ ret_val = yield(response)
1886
+ end
1887
+ }
1888
+
1889
+ ret_val
1675
1890
  end
1676
1891
  imap_command :list
1677
1892
 
1678
- def lsub(tag, ref_name, mbox_name, &block)
1679
- @engine.lsub(@token, tag, ref_name, mbox_name, &block)
1893
+ def lsub(tag, ref_name, mbox_name)
1894
+ ret_val = nil
1895
+ @engine.lsub(@token, tag, ref_name, mbox_name) {|res|
1896
+ for response in res
1897
+ ret_val = yield(response)
1898
+ end
1899
+ }
1900
+
1901
+ ret_val
1680
1902
  end
1681
1903
  imap_command :lsub
1682
1904
 
1683
- def status(tag, mbox_name, data_item_group, &block)
1684
- @engine.status(@token, tag, mbox_name, data_item_group, &block)
1905
+ def status(tag, mbox_name, data_item_group)
1906
+ ret_val = nil
1907
+ @engine.status(@token, tag, mbox_name, data_item_group) {|res|
1908
+ for response in res
1909
+ ret_val = yield(response)
1910
+ end
1911
+ }
1912
+
1913
+ ret_val
1685
1914
  end
1686
1915
  imap_command :status
1687
1916
 
1688
- def append(tag, mbox_name, *opt_args, msg_text, &block)
1689
- @engine.append(@token, tag, mbox_name, *opt_args, msg_text, &block)
1917
+ def append(tag, mbox_name, *opt_args, msg_text)
1918
+ ret_val = nil
1919
+ @engine.append(@token, tag, mbox_name, *opt_args, msg_text) {|res|
1920
+ for response in res
1921
+ ret_val = yield(response)
1922
+ end
1923
+ }
1924
+
1925
+ ret_val
1690
1926
  end
1691
1927
  imap_command :append
1692
1928
 
1693
- def check(tag, &block)
1694
- @engine.check(@token, tag, &block)
1929
+ def check(tag)
1930
+ ret_val = nil
1931
+ @engine.check(@token, tag) {|res|
1932
+ for response in res
1933
+ ret_val = yield(response)
1934
+ end
1935
+ }
1936
+
1937
+ ret_val
1695
1938
  end
1696
1939
  imap_command :check
1697
1940
 
1698
- def close(tag, &block)
1941
+ def close(tag)
1942
+ ret_val = nil
1699
1943
  old_token = @token
1700
1944
  @token = nil
1701
-
1702
- yield response_stream(tag) {|res|
1703
- @engine.close(old_token, tag) {|bulk_res|
1704
- for r in bulk_res
1705
- res << r
1706
- end
1707
- }
1945
+ @engine.close(old_token, tag) {|res|
1946
+ for response in res
1947
+ ret_val = yield(response)
1948
+ end
1708
1949
  }
1950
+
1951
+ ret_val
1709
1952
  end
1710
1953
  imap_command :close
1711
1954
 
1712
1955
  def expunge(tag)
1713
- yield response_stream(tag) {|res|
1714
- @engine.expunge(@token, tag) {|bulk_res|
1715
- for r in bulk_res
1716
- res << r
1717
- end
1718
- }
1956
+ ret_val = nil
1957
+ @engine.expunge(@token, tag) {|res|
1958
+ for response in res
1959
+ ret_val = yield(response)
1960
+ end
1719
1961
  }
1962
+
1963
+ ret_val
1720
1964
  end
1721
1965
  imap_command :expunge
1722
1966
 
1723
1967
  def search(tag, *cond_args, uid: false)
1724
- yield response_stream(tag) {|res|
1725
- @engine.search(@token, tag, *cond_args, uid: uid) {|bulk_res|
1726
- for r in bulk_res
1727
- res << r
1728
- end
1729
- }
1968
+ ret_val = nil
1969
+ @engine.search(@token, tag, *cond_args, uid: uid) {|res|
1970
+ for response in res
1971
+ ret_val = yield(response)
1972
+ end
1730
1973
  }
1974
+
1975
+ ret_val
1731
1976
  end
1732
1977
  imap_command :search
1733
1978
 
1734
1979
  def fetch(tag, msg_set, data_item_group, uid: false)
1735
- yield response_stream(tag) {|res|
1736
- @engine.fetch(@token, tag, msg_set, data_item_group, uid: uid) {|bulk_res|
1737
- for r in bulk_res
1738
- res << r
1739
- end
1740
- }
1980
+ ret_val = nil
1981
+ @engine.fetch(@token, tag, msg_set, data_item_group, uid: uid) {|res|
1982
+ for response in res
1983
+ ret_val = yield(response)
1984
+ end
1741
1985
  }
1986
+
1987
+ ret_val
1742
1988
  end
1743
1989
  imap_command :fetch
1744
1990
 
1745
1991
  def store(tag, msg_set, data_item_name, data_item_value, uid: false)
1746
- yield response_stream(tag) {|res|
1747
- @engine.store(@token, tag, msg_set, data_item_name, data_item_value, uid: uid) {|bulk_res|
1748
- for r in bulk_res
1749
- res << r
1750
- end
1751
- }
1992
+ ret_val = nil
1993
+ @engine.store(@token, tag, msg_set, data_item_name, data_item_value, uid: uid) {|res|
1994
+ for response in res
1995
+ ret_val = yield(response)
1996
+ end
1752
1997
  }
1998
+
1999
+ ret_val
1753
2000
  end
1754
2001
  imap_command :store
1755
2002
 
1756
- def copy(tag, msg_set, mbox_name, uid: false, &block)
1757
- @engine.copy(@token, tag, msg_set, mbox_name, uid: uid, &block)
2003
+ def copy(tag, msg_set, mbox_name, uid: false)
2004
+ ret_val = nil
2005
+ @engine.copy(@token, tag, msg_set, mbox_name, uid: uid) {|res|
2006
+ for response in res
2007
+ ret_val = yield(response)
2008
+ end
2009
+ }
2010
+
2011
+ ret_val
1758
2012
  end
1759
2013
  imap_command :copy
1760
2014
 
1761
- def idle(tag, client_input_gets, server_output_write, connection_timer, &block)
1762
- @engine.idle(@token, tag, client_input_gets, server_output_write, connection_timer, &block)
2015
+ def idle(tag, client_input_gets, server_output_write, connection_timer)
2016
+ ret_val = nil
2017
+ @engine.idle(@token, tag, client_input_gets, server_output_write, connection_timer) {|res|
2018
+ for response in res
2019
+ ret_val = yield(response)
2020
+ end
2021
+ }
2022
+
2023
+ ret_val
1763
2024
  end
1764
2025
  imap_command :idle
1765
2026
  end
1766
2027
 
1767
2028
  # alias
1768
- Decoder::Engine = UserMailboxDecoder::Engine
2029
+ Decoder::Engine = UserMailboxDecoder::Engine
2030
+ Decoder::BulkResponse = UserMailboxDecoder::BulkResponse
1769
2031
 
1770
2032
  def Decoder.encode_delivery_target_mailbox(username, mbox_name)
1771
2033
  "b64user-mbox #{Protocol.encode_base64(username)} #{mbox_name}"
@@ -1787,6 +2049,7 @@ module RIMS
1787
2049
  @auth = auth
1788
2050
  @last_user_cache_key_username = nil
1789
2051
  @last_user_cache_value_engine = nil
2052
+ @logger.debug("RIMS::Protocol::MailDeliveryDecoder#initialize at #{self}") if @logger.debug?
1790
2053
  end
1791
2054
 
1792
2055
  def engine_cached?(username)
@@ -1834,6 +2097,8 @@ module RIMS
1834
2097
  end
1835
2098
 
1836
2099
  def cleanup
2100
+ @logger.debug("RIMS::Protocol::MailDeliveryDecoder#cleanup at #{self}") if @logger.debug?
2101
+
1837
2102
  release_engine_cache
1838
2103
  @drb_services = nil unless @drb_services.nil?
1839
2104
  @auth = nil unless @auth.nil?
@@ -1846,9 +2111,9 @@ module RIMS
1846
2111
  nil
1847
2112
  end
1848
2113
 
1849
- def logout(tag)
1850
- @next_decoder = LogoutDecoder.new(self)
1851
- yield(make_logout_response(tag))
2114
+ def logout(tag, &block)
2115
+ @next_decoder = LogoutDecoder.new(self, @logger)
2116
+ make_logout_response(tag, &block)
1852
2117
  end
1853
2118
  imap_command :logout
1854
2119
 
@@ -1856,85 +2121,87 @@ module RIMS
1856
2121
  private :standard_capability
1857
2122
 
1858
2123
  def capability(tag)
1859
- standard_capability(tag) {|res|
1860
- yield res.map{|line|
1861
- if (line.start_with? '* CAPABILITY ') then
1862
- line.strip + " X-RIMS-MAIL-DELIVERY-USER\r\n"
1863
- else
1864
- line
1865
- end
1866
- }
2124
+ standard_capability(tag) {|response|
2125
+ if (response.start_with? '* CAPABILITY ') then
2126
+ yield(response.strip + " X-RIMS-MAIL-DELIVERY-USER\r\n")
2127
+ else
2128
+ yield(response)
2129
+ end
1867
2130
  }
1868
2131
  end
1869
2132
  imap_command :capability
1870
2133
 
1871
2134
  def make_not_allowed_command_response(tag)
1872
- [ "#{tag} NO not allowed command on mail delivery user\r\n" ]
2135
+ yield("#{tag} NO not allowed command on mail delivery user\r\n")
1873
2136
  end
1874
2137
  private :make_not_allowed_command_response
1875
2138
 
1876
- def select(tag, mbox_name)
1877
- yield(make_not_allowed_command_response(tag))
2139
+ def select(tag, mbox_name, &block)
2140
+ make_not_allowed_command_response(tag, &block)
1878
2141
  end
1879
2142
  imap_command :select
1880
2143
 
1881
- def examine(tag, mbox_name)
1882
- yield(make_not_allowed_command_response(tag))
2144
+ def examine(tag, mbox_name, &block)
2145
+ make_not_allowed_command_response(tag, &block)
1883
2146
  end
1884
2147
  imap_command :examine
1885
2148
 
1886
- def create(tag, mbox_name)
1887
- yield(make_not_allowed_command_response(tag))
2149
+ def create(tag, mbox_name, &block)
2150
+ make_not_allowed_command_response(tag, &block)
1888
2151
  end
1889
2152
  imap_command :create
1890
2153
 
1891
- def delete(tag, mbox_name)
1892
- yield(make_not_allowed_command_response(tag))
2154
+ def delete(tag, mbox_name, &block)
2155
+ make_not_allowed_command_response(tag, &block)
1893
2156
  end
1894
2157
  imap_command :delete
1895
2158
 
1896
- def rename(tag, src_name, dst_name)
1897
- yield(make_not_allowed_command_response(tag))
2159
+ def rename(tag, src_name, dst_name, &block)
2160
+ make_not_allowed_command_response(tag, &block)
1898
2161
  end
1899
2162
  imap_command :rename
1900
2163
 
1901
- def subscribe(tag, mbox_name)
1902
- yield(make_not_allowed_command_response(tag))
2164
+ def subscribe(tag, mbox_name, &block)
2165
+ make_not_allowed_command_response(tag, &block)
1903
2166
  end
1904
2167
  imap_command :subscribe
1905
2168
 
1906
- def unsubscribe(tag, mbox_name)
1907
- yield(make_not_allowed_command_response(tag))
2169
+ def unsubscribe(tag, mbox_name, &block)
2170
+ make_not_allowed_command_response(tag, &block)
1908
2171
  end
1909
2172
  imap_command :unsubscribe
1910
2173
 
1911
- def list(tag, ref_name, mbox_name)
1912
- yield(make_not_allowed_command_response(tag))
2174
+ def list(tag, ref_name, mbox_name, &block)
2175
+ make_not_allowed_command_response(tag, &block)
1913
2176
  end
1914
2177
  imap_command :list
1915
2178
 
1916
- def lsub(tag, ref_name, mbox_name)
1917
- yield(make_not_allowed_command_response(tag))
2179
+ def lsub(tag, ref_name, mbox_name, &block)
2180
+ make_not_allowed_command_response(tag, &block)
1918
2181
  end
1919
2182
  imap_command :lsub
1920
2183
 
1921
- def status(tag, mbox_name, data_item_group)
1922
- yield(make_not_allowed_command_response(tag))
2184
+ def status(tag, mbox_name, data_item_group, &block)
2185
+ make_not_allowed_command_response(tag, &block)
1923
2186
  end
1924
2187
  imap_command :status
1925
2188
 
1926
- def deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, res)
2189
+ def deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine)
1927
2190
  user_decoder = UserMailboxDecoder.new(self, engine, @auth, @logger)
1928
- user_decoder.append(tag, mbox_name, *opt_args, msg_text) {|append_response|
1929
- if (append_response.last.split(' ', 3)[1] == 'OK') then
2191
+ begin
2192
+ last_response = nil
2193
+ user_decoder.append(tag, mbox_name, *opt_args, msg_text) {|response|
2194
+ last_response = response
2195
+ yield(response)
2196
+ }
2197
+ if (last_response.split(' ', 3)[1] == 'OK') then
1930
2198
  @logger.info("message delivery: successed to deliver #{msg_text.bytesize} octets message.")
1931
2199
  else
1932
2200
  @logger.info("message delivery: failed to deliver message.")
1933
2201
  end
1934
- for response_data in append_response
1935
- res << response_data
1936
- end
1937
- }
2202
+ ensure
2203
+ user_decoder.cleanup(not_cleanup_parent: true)
2204
+ end
1938
2205
  end
1939
2206
  private :deliver_to_user
1940
2207
 
@@ -1944,62 +2211,65 @@ module RIMS
1944
2211
 
1945
2212
  if (@auth.user? username) then
1946
2213
  if (engine_cached? username) then
1947
- res = []
1948
2214
  engine = engine_cache(username)
1949
- deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, res)
2215
+ deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine) {|response|
2216
+ yield(response)
2217
+ }
1950
2218
  else
1951
- res = response_stream(tag) {|stream_res|
1952
- engine = store_engine_cache(username) {
1953
- self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|msg| stream_res << msg << :flush }
2219
+ engine = store_engine_cache(username) {
2220
+ self.class.make_engine_and_recovery_if_needed(@drb_services, username, logger: @logger) {|untagged_response|
2221
+ yield(untagged_response)
2222
+ yield(:flush)
1954
2223
  }
1955
- deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine, stream_res)
2224
+ }
2225
+ deliver_to_user(tag, username, mbox_name, opt_args, msg_text, engine) {|response|
2226
+ yield(response)
1956
2227
  }
1957
2228
  end
1958
- yield(res)
1959
2229
  else
1960
2230
  @logger.info('message delivery: not found a user.')
1961
- yield([ "#{tag} NO not found a user and couldn't deliver a message to the user's mailbox\r\n" ])
2231
+ yield("#{tag} NO not found a user and couldn't deliver a message to the user's mailbox\r\n")
1962
2232
  end
1963
2233
  end
1964
2234
  imap_command :append
1965
2235
 
1966
- def check(tag)
1967
- yield(make_not_allowed_command_response(tag))
2236
+ def check(tag, &block)
2237
+ make_not_allowed_command_response(tag, &block)
1968
2238
  end
1969
2239
  imap_command :check
1970
2240
 
1971
- def close(tag)
1972
- yield(make_not_allowed_command_response(tag))
2241
+ def close(tag, &block)
2242
+ make_not_allowed_command_response(tag, &block)
1973
2243
  end
1974
2244
  imap_command :close
1975
2245
 
1976
- def expunge(tag)
1977
- yield(make_not_allowed_command_response(tag))
2246
+ def expunge(tag, &block)
2247
+ make_not_allowed_command_response(tag, &block)
1978
2248
  end
1979
2249
  imap_command :expunge
1980
2250
 
1981
- def search(tag, *cond_args, uid: false)
1982
- yield(make_not_allowed_command_response(tag))
2251
+ def search(tag, *cond_args, uid: false, &block)
2252
+ make_not_allowed_command_response(tag, &block)
1983
2253
  end
1984
2254
  imap_command :search
1985
2255
 
1986
- def fetch(tag, msg_set, data_item_group, uid: false)
1987
- yield(make_not_allowed_command_response(tag))
2256
+ def fetch(tag, msg_set, data_item_group, uid: false, &block)
2257
+ make_not_allowed_command_response(tag, &block)
1988
2258
  end
1989
2259
  imap_command :fetch
1990
2260
 
1991
- def store(tag, msg_set, data_item_name, data_item_value, uid: false)
1992
- yield(make_not_allowed_command_response(tag))
2261
+ def store(tag, msg_set, data_item_name, data_item_value, uid: false, &block)
2262
+ make_not_allowed_command_response(tag, &block)
1993
2263
  end
1994
2264
  imap_command :store
1995
2265
 
1996
- def copy(tag, msg_set, mbox_name, uid: false)
1997
- yield(make_not_allowed_command_response(tag))
2266
+ def copy(tag, msg_set, mbox_name, uid: false, &block)
2267
+ make_not_allowed_command_response(tag, &block)
1998
2268
  end
1999
2269
  imap_command :copy
2000
2270
 
2001
- def idle(tag, client_input_gets, server_output_write, connection_timer)
2002
- yield(make_not_allowed_command_response(tag))
2271
+ def idle(tag, client_input_gets, server_output_write, connection_timer, &block)
2272
+ make_not_allowed_command_response(tag, &block)
2003
2273
  end
2004
2274
  imap_command :idle
2005
2275
  end