rims 0.2.6 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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