rims 0.2.5 → 0.3.0

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