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.
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module RIMS
4
- VERSION = '0.2.5'
4
+ VERSION = '0.3.0'
5
5
  MAILBOX_DATA_STRUCTURE_VERSION = 'mailbox.2'
6
6
  end
7
7
 
@@ -78,6 +78,8 @@ task :imap_append do
78
78
  end
79
79
  end
80
80
 
81
+ CLOBBER.include('imap_append')
82
+
81
83
  desc 'run server'
82
84
  task :run_server do
83
85
  run_server_conf('imap_server', USER_CONF, *%w[ -v debug -l debug --imap-host=localhost --imap-port=14300 ]) do
@@ -85,7 +87,7 @@ task :run_server do
85
87
  end
86
88
  end
87
89
 
88
- CLOBBER.include('imap_append')
90
+ CLOBBER.include('imap_server')
89
91
 
90
92
  # Local Variables:
91
93
  # mode: Ruby
@@ -5,10 +5,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require 'rims/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = "rims"
8
+ spec.name = 'rims'
9
9
  spec.version = RIMS::VERSION
10
- spec.authors = ["TOKI Yoshinori"]
11
- spec.email = ["toki@freedom.ne.jp"]
10
+ spec.authors = ['TOKI Yoshinori']
11
+ spec.email = ['toki@freedom.ne.jp']
12
12
  spec.summary = %q{RIMS is Ruby IMap Server}
13
13
  spec.description = <<-'EOF'
14
14
  RIMS is Ruby IMap Server.
@@ -16,22 +16,24 @@ Gem::Specification.new do |spec|
16
16
  server can run as a daemon, mailboxes are provided and messages
17
17
  can be delivered to them.
18
18
  EOF
19
- spec.homepage = "https://github.com/y10k/rims"
20
- spec.license = "MIT"
19
+ spec.homepage = 'https://github.com/y10k/rims'
20
+ spec.license = 'MIT'
21
21
 
22
22
  spec.files = `git ls-files`.split($/)
23
23
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
24
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
- spec.require_paths = ["lib"]
25
+ spec.require_paths = ['lib']
26
26
 
27
27
  spec.required_ruby_version = '>= 2.0.0'
28
28
 
29
- spec.add_runtime_dependency "riser", '>= 0.1.8'
30
- spec.add_runtime_dependency "logger-joint"
31
- spec.add_development_dependency "bundler"
32
- spec.add_development_dependency "rake"
33
- spec.add_development_dependency "test-unit"
34
- spec.add_development_dependency "rdoc"
29
+ spec.add_runtime_dependency 'rims-rfc822', '>= 0.2.2'
30
+ spec.add_runtime_dependency 'riser', '>= 0.2.0'
31
+ spec.add_runtime_dependency 'logger-joint'
32
+ spec.add_development_dependency 'bundler'
33
+ spec.add_development_dependency 'rake'
34
+ spec.add_development_dependency 'test-unit'
35
+ spec.add_development_dependency 'rdoc'
36
+ spec.add_development_dependency 'irb'
35
37
  end
36
38
 
37
39
  # Local Variables:
@@ -119,6 +119,7 @@ module RIMS::Test
119
119
  '--no-daemonize' => [ %W[ -f #{BASE_DIR}/config.yml --no-daemonize ] ],
120
120
  '--daemon-debug' => [ %W[ -f #{BASE_DIR}/config.yml --daemon-debug ] ],
121
121
  '--no-daemon-debug' => [ %W[ -f #{BASE_DIR}/config.yml --no-daemon-debug ] ],
122
+ '--daemon-umask' => [ %W[ -f #{BASE_DIR}/config.yml --daemon-umask=0022 ] ],
122
123
  '--status-file' => [ %W[ -f #{BASE_DIR}/config.yml --status-file=status.yml ] ],
123
124
  '--privilege-user' => [ %W[ -f #{BASE_DIR}/config.yml --privilege-user=#{Process::UID.eid} ] ],
124
125
  '--privilege-group' => [ %W[ -f #{BASE_DIR}/config.yml --privilege-group=#{Process::GID.eid} ] ],
@@ -140,11 +141,30 @@ module RIMS::Test
140
141
  '--read-polling-interval' => [ %W[ -f #{BASE_DIR}/config.yml --read-polling-interval=5 ] ],
141
142
  '--command-wait-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --command-wait-timeout=3600 ] ],
142
143
 
144
+ # protocol:
145
+ '--line-length-limit' => [ %W[ -f #{BASE_DIR}/config.yml --line-length-limit=16384 ] ],
146
+ '--literal-size-limit' => [ %W[ -f #{BASE_DIR}/config.yml --literal-size-limit=16777216 ] ],
147
+ '--command-size-limit' => [ %W[ -f #{BASE_DIR}/config.yml --command-size-limit=16777216 ] ],
148
+
149
+ # charset aliases:
150
+ '--use-default-charset-aliases' => [ %W[ -f #{BASE_DIR}/config.yml --use-default-charset-aliases ] ],
151
+ '--no-use-default-charset-aliases' => [ %W[ -f #{BASE_DIR}/config.yml --no-use-default-charset-aliases ] ],
152
+ '--add-charset-alias' => [ %W[ -f #{BASE_DIR}/config.yml --no-use-default-charset-aliases --add-charset-alias=iso-2022-jp,CP50221 ] ],
153
+
154
+ # charset convert_options:
155
+ '--replace-charset-invalid' => [ %W[ -f #{BASE_DIR}/config.yml --replace-charset-invalid ] ],
156
+ '--no-replace-charset-invalid' => [ %W[ -f #{BASE_DIR}/config.yml --no-replace-charset-invalid ] ],
157
+ '--replace-charset-undef' => [ %W[ -f #{BASE_DIR}/config.yml --replace-charset-undef ] ],
158
+ '--no-replace-charset-undef' => [ %W[ -f #{BASE_DIR}/config.yml --no-replace-charset-undef ] ],
159
+ '--charset-replaced-mark' => [ %W[ -f #{BASE_DIR}/config.yml --charset-replaced-mark=? ] ],
160
+
143
161
  # drb_services:
144
162
  '--drb-process-num' => [ %W[ -f #{BASE_DIR}/config.yml --drb-process-num=4 ] ],
163
+ '--drb-load-limit' => [ %W[ -f #{BASE_DIR}/config.yml --drb-load-limit=134217728 ] ],
145
164
 
146
165
  # drb_services engine:
147
166
  '--bulk-response-count' => [ %W[ -f #{BASE_DIR}/config.yml --bulk-response-count=128 ] ],
167
+ '--bulk-response-size' => [ %W[ -f #{BASE_DIR}/config.yml --bulk-response-size=33554432 ] ],
148
168
  '--read-lock-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --read-lock-timeout=10 ] ],
149
169
  '--write-lock-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --write-lock-timeout=10 ] ],
150
170
  '--clenup-write-lock-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --write-lock-timeout=5 ] ],
@@ -334,6 +354,35 @@ module RIMS::Test
334
354
  assert_equal(0, status.exitstatus)
335
355
  end
336
356
 
357
+ data('default' => [ false, %w[] ],
358
+ '-r' => [ false, %w[ -r prime ] ],
359
+ '--required-feature' => [ false, %w[ --required-feature=prime ] ],
360
+ '--format=yaml' => [ false, %w[ --format=yaml ] ],
361
+ '--format=json' => [ false, %w[ --format=json ] ],
362
+
363
+ # deplicated options
364
+ 'deplicated:--load-library' => [ true, %w[ --load-library=prime ] ])
365
+ def test_environment(data)
366
+ deplicated, options = data
367
+
368
+ stdout, stderr, status = Open3.capture3('rims', 'environment', *options)
369
+ pp [ stdout, stderr, status ] if $DEBUG
370
+
371
+ assert_equal(0, status.exitstatus)
372
+ if (deplicated) then
373
+ assert_match(/^warning:/, stderr)
374
+ else
375
+ assert_equal('', stderr)
376
+ end
377
+
378
+ assert_match(/RIMS Environment/, stdout)
379
+ assert_match(/RUBY VERSION.*#{Regexp.quote(RUBY_DESCRIPTION)}/, stdout)
380
+ assert_match(/RIMS VERSION.*#{Regexp.quote(RIMS::VERSION)}/, stdout)
381
+ assert_match(/AUTHENTICATION PLUG-IN.*plain/m, stdout)
382
+ assert_match(/AUTHENTICATION PLUG-IN.*hash/m, stdout)
383
+ assert_match(/KEY-VALUE STORE PLUG-IN.*gdbm/m, stdout)
384
+ end
385
+
337
386
  tls_dir = Pathname(__FILE__).parent.parent / "tls"
338
387
  TLS_CA_CERT = tls_dir / 'ca.cert'
339
388
  TLS_SERVER_CERT = tls_dir / 'server_localhost.cert'
@@ -1203,7 +1252,7 @@ Hello world.
1203
1252
  assert_equal(seqno[1], imap_search.call([ 'ANSWERED' ])) # *a
1204
1253
  assert_equal(seqno[3], imap_search.call([ 'BCC', 'foo' ])) # *b
1205
1254
  assert_equal(seqno[1], imap_search.call([ 'BEFORE', @mpart_mail.date ]))
1206
- assert_equal(seqno[1, 3], imap_search.call([ 'BODY', 'Hello world.' ]))
1255
+ assert_equal(seqno[1, 2, 3], imap_search.call([ 'BODY', 'Hello world.' ]))
1207
1256
  assert_equal(seqno[3], imap_search.call([ 'CC', 'kate' ]))
1208
1257
  assert_equal(seqno[], imap_search.call([ 'DELETED' ]))
1209
1258
  assert_equal(seqno[2], imap_search.call([ 'DRAFT' ]))
@@ -1277,49 +1326,112 @@ Hello world.
1277
1326
  envelope = lambda{|mail|
1278
1327
  Net::IMAP::Envelope.new(mail.header['Date'],
1279
1328
  mail.header['Subject'],
1280
- mail.from ? mail.from.map{|addr| Net::IMAP::Address.new(*addr) } : nil,
1281
- mail.reply_to ? mail.reply_to.map{|addr| Net::IMAP::Address.new(*addr) } : nil,
1282
- mail.sender ? mail.sender.map{|addr| Net::IMAP::Address.new(*addr) } : nil,
1283
- mail.to ? mail.to.map{|addr| Net::IMAP::Address.new(*addr) } : nil,
1284
- mail.cc ? mail.cc.map{|addr| Net::IMAP::Address.new(*addr) } : nil,
1285
- mail.bcc ? mail.bcc.map{|addr| Net::IMAP::Address.new(*addr) } : nil,
1329
+ mail.from ? mail.from.map{|addr| Net::IMAP::Address.new(*addr.to_a) } : nil,
1330
+ mail.reply_to ? mail.reply_to.map{|addr| Net::IMAP::Address.new(*addr.to_a) } : nil,
1331
+ mail.sender ? mail.sender.map{|addr| Net::IMAP::Address.new(*addr.to_a) } : nil,
1332
+ mail.to ? mail.to.map{|addr| Net::IMAP::Address.new(*addr.to_a) } : nil,
1333
+ mail.cc ? mail.cc.map{|addr| Net::IMAP::Address.new(*addr.to_a) } : nil,
1334
+ mail.bcc ? mail.bcc.map{|addr| Net::IMAP::Address.new(*addr.to_a) } : nil,
1286
1335
  mail.header['In-Reply-To'],
1287
1336
  mail.header['Message-Id'])
1288
1337
  }
1289
- body_type = lambda{|mail|
1290
- case (mail.media_main_type_upcase)
1291
- when 'TEXT'
1338
+ body_type = lambda{|mail, extension=false|
1339
+ body_params = lambda{|params|
1340
+ if (params && ! params.empty?) then
1341
+ Hash[params.map{|n, v| [ n.upcase, v ] }]
1342
+ end
1343
+ }
1344
+ body_disposition = lambda{|mail|
1345
+ if (mail.content_disposition) then
1346
+ Net::IMAP::ContentDisposition.new(mail.content_disposition_upcase,
1347
+ body_params[mail.content_disposition_parameter_list])
1348
+ end
1349
+ }
1350
+ body_language = lambda{|mail|
1351
+ if (mail.content_language) then
1352
+ if (mail.content_language.length > 1) then
1353
+ mail.content_language_upcase
1354
+ else
1355
+ mail.content_language_upcase[0]
1356
+ end
1357
+ end
1358
+ }
1359
+ if (mail.text?) then
1292
1360
  Net::IMAP::BodyTypeText.new(mail.media_main_type_upcase,
1293
1361
  mail.media_sub_type_upcase,
1294
- Hash[mail.content_type_parameters.map{|n, v| [ n.upcase, v ] }],
1362
+ body_params[mail.content_type_parameter_list],
1295
1363
  mail.header['Content-Id'],
1296
1364
  mail.header['Content-Description'],
1297
1365
  mail.header.fetch_upcase('Content-Transfer-Encoding'),
1298
1366
  mail.raw_source.bytesize,
1299
- mail.raw_source.each_line.count)
1300
- when 'MESSAGE'
1367
+ mail.raw_source.each_line.count,
1368
+ *(
1369
+ if (extension) then
1370
+ [ mail.header['Content-MD5'],
1371
+ body_disposition[mail],
1372
+ body_language[mail],
1373
+ [ mail.header['Content-Location'] ]
1374
+ ]
1375
+ else
1376
+ []
1377
+ end
1378
+ ))
1379
+ elsif (mail.message?) then
1301
1380
  Net::IMAP::BodyTypeMessage.new(mail.media_main_type_upcase,
1302
1381
  mail.media_sub_type_upcase,
1303
- Hash[mail.content_type_parameters.map{|n, v| [ n.upcase, v ] }],
1382
+ body_params[mail.content_type_parameter_list],
1304
1383
  mail.header['Content-Id'],
1305
1384
  mail.header['Content-Description'],
1306
1385
  mail.header.fetch_upcase('Content-Transfer-Encoding'),
1307
1386
  mail.raw_source.bytesize,
1308
1387
  envelope[mail.message],
1309
- body_type[mail.message],
1310
- mail.raw_source.each_line.count)
1311
- when 'MULTIPART'
1388
+ body_type[mail.message, extension],
1389
+ mail.raw_source.each_line.count,
1390
+ *(
1391
+ if (extension) then
1392
+ [ mail.header['Content-MD5'],
1393
+ body_disposition[mail],
1394
+ body_language[mail],
1395
+ [ mail.header['Content-Location'] ]
1396
+ ]
1397
+ else
1398
+ []
1399
+ end
1400
+ ))
1401
+ elsif (mail.multipart?) then
1312
1402
  Net::IMAP::BodyTypeMultipart.new(mail.media_main_type_upcase,
1313
1403
  mail.media_sub_type_upcase,
1314
- mail.parts.map{|m| body_type[m] })
1404
+ mail.parts.map{|m| body_type[m, extension] },
1405
+ *(
1406
+ if (extension) then
1407
+ [ body_params[mail.content_type_parameter_list],
1408
+ body_disposition[mail],
1409
+ body_language[mail],
1410
+ [ mail.header['Content-Location'] ]
1411
+ ]
1412
+ else
1413
+ []
1414
+ end
1415
+ ))
1315
1416
  else
1316
1417
  Net::IMAP::BodyTypeBasic.new(mail.media_main_type_upcase,
1317
1418
  mail.media_sub_type_upcase,
1318
- Hash[mail.content_type_parameters.map{|n, v| [ n.upcase, v ] }],
1419
+ body_params[mail.content_type_parameter_list],
1319
1420
  mail.header['Content-Id'],
1320
1421
  mail.header['Content-Description'],
1321
1422
  mail.header.fetch_upcase('Content-Transfer-Encoding'),
1322
- mail.raw_source.bytesize)
1423
+ mail.raw_source.bytesize,
1424
+ *(
1425
+ if (extension) then
1426
+ [ mail.header['Content-MD5'],
1427
+ body_disposition[mail],
1428
+ body_language[mail],
1429
+ [ mail.header['Content-Location'] ]
1430
+ ]
1431
+ else
1432
+ []
1433
+ end
1434
+ ))
1323
1435
  end
1324
1436
  }
1325
1437
 
@@ -1406,9 +1518,9 @@ Hello world.
1406
1518
  imap_fetch.call(msg_set[1..-1], 'BODY'))
1407
1519
 
1408
1520
  assert_equal(fetch_data[
1409
- [ 1, { 'BODYSTRUCTURE' => body_type[@simple_mail] } ],
1410
- [ 2, { 'BODYSTRUCTURE' => body_type[@mpart_mail] } ],
1411
- [ 3, { 'BODYSTRUCTURE' => body_type[@mime_subject_mail] } ]
1521
+ [ 1, { 'BODYSTRUCTURE' => body_type[@simple_mail, true] } ],
1522
+ [ 2, { 'BODYSTRUCTURE' => body_type[@mpart_mail, true] } ],
1523
+ [ 3, { 'BODYSTRUCTURE' => body_type[@mime_subject_mail, true] } ]
1412
1524
  ],
1413
1525
  imap_fetch.call(msg_set[1..-1], 'BODYSTRUCTURE'))
1414
1526
 
@@ -55,7 +55,8 @@ module RIMS::Test
55
55
  pub1.publish('msg1')
56
56
  pub2.publish('msg2')
57
57
 
58
- error = assert_raise(RuntimeError) { pub3.publish('msg3') }
58
+ error = assert_raise(RIMS::ServerResponseChannelPublishError) { pub3.publish('msg3') }
59
+ pp error, error.optional_data if $DEBUG
59
60
  assert_match(/detached/, error.message)
60
61
 
61
62
  assert_equal(%w[ msg2 ], sub1.enum_for(:fetch).to_a)
@@ -4,7 +4,19 @@ require 'rims'
4
4
  require 'test/unit'
5
5
 
6
6
  module RIMS::Test
7
- class TestError < Test::Unit::TestCase
7
+ class ErrorTest < Test::Unit::TestCase
8
+ def test_optional_data
9
+ error = RIMS::Error.new('test', foo: 1, bar: '2')
10
+ assert_equal('test', error.message)
11
+ assert_equal({ foo: 1, bar: '2' }, error.optional_data)
12
+ end
13
+
14
+ def test_no_optional_data
15
+ error = RIMS::Error.new('test')
16
+ assert_equal('test', error.message)
17
+ assert_predicate(error.optional_data, :empty?)
18
+ end
19
+
8
20
  def test_trace_error_chain
9
21
  exception = assert_raise(RuntimeError) {
10
22
  begin
@@ -32,6 +44,26 @@ module RIMS::Test
32
44
  'error level 0'
33
45
  ], errors.map(&:message))
34
46
  end
47
+
48
+ def test_optional_data_block
49
+ count = 0
50
+ error = RIMS::Error.new('test', foo: 1, bar: '2')
51
+ RIMS::Error.optional_data(error) do |e, data|
52
+ count += 1
53
+ assert_equal(error, e)
54
+ assert_equal({ foo: 1, bar: '2' }, data)
55
+ end
56
+ assert_equal(1, count)
57
+ end
58
+
59
+ data('not a RIMS::Error' => StandardError.new('test'),
60
+ 'no optional data' => RIMS::Error.new('test'))
61
+ def test_no_optional_data_block(data)
62
+ error = data
63
+ RIMS::Error.optional_data(error) do |e, data|
64
+ flunk
65
+ end
66
+ end
35
67
  end
36
68
  end
37
69
 
@@ -51,7 +51,7 @@ module RIMS::Test
51
51
  assert_equal('write-lock wait timeout', error.message)
52
52
  end
53
53
 
54
- def calculate_threa_work_seconds
54
+ def calculate_thread_work_seconds
55
55
  t0 = Time.now
56
56
  1000.times{|i| i.succ }
57
57
  t1 = Time.now
@@ -60,10 +60,10 @@ module RIMS::Test
60
60
 
61
61
  wait_seconds
62
62
  end
63
- private :calculate_threa_work_seconds
63
+ private :calculate_thread_work_seconds
64
64
 
65
65
  def test_read_write_lock_multithread
66
- lock_wait_seconds = calculate_threa_work_seconds
66
+ lock_wait_seconds = calculate_thread_work_seconds
67
67
 
68
68
  count = 0
69
69
  read_thread_num = 10
@@ -116,7 +116,7 @@ module RIMS::Test
116
116
  def test_write_lock_timeout_detach
117
117
  logger = Logger.new(STDOUT)
118
118
  logger.level = ($DEBUG) ? Logger::DEBUG : Logger::FATAL
119
- wait_seconds = calculate_threa_work_seconds
119
+ wait_seconds = calculate_thread_work_seconds
120
120
 
121
121
  t_list = []
122
122
  assert_nil(RIMS::ReadWriteLock.write_lock_timeout_detach(wait_seconds, wait_seconds * 2, logger: logger ) {|timeout_seconds|
@@ -61,7 +61,7 @@ module RIMS::Test
61
61
  end
62
62
  end
63
63
 
64
- class PasswordHashSourceEntry < Test::Unit::TestCase
64
+ class PasswordHashSourceEntryTest < Test::Unit::TestCase
65
65
  def setup
66
66
  @digest_factory = Digest::SHA256
67
67
  @hash_type = 'SHA256'
@@ -127,6 +127,7 @@ module RIMS::Test
127
127
  cmd_name, cmd_args, cmd_client_output = parse_imap_command(tag, imap_command_message)
128
128
  normalized_cmd_name = RIMS::Protocol::Decoder.imap_command_normalize(cmd_name)
129
129
  cmd_id = RIMS::Protocol::Decoder::IMAP_CMDs[normalized_cmd_name] or flunk("not a imap command: #{cmd_name}")
130
+ cmd_kw_args = {}
130
131
 
131
132
  input = nil
132
133
  output = nil
@@ -135,8 +136,10 @@ module RIMS::Test
135
136
  output = StringIO.new('', 'w')
136
137
  input_gets = input.method(:gets)
137
138
  output_write = lambda{|res|
138
- for line in res
139
- output << line
139
+ for data in res
140
+ if (data != :flush) then
141
+ output << data
142
+ end
140
143
  end
141
144
  }
142
145
  inout_args = [ input_gets, output_write ]
@@ -144,30 +147,29 @@ module RIMS::Test
144
147
  cmd_args = inout_args + cmd_args
145
148
  end
146
149
  unless (uid.nil?) then
147
- cmd_args += [ { uid: uid } ]
150
+ cmd_kw_args[:uid] = uid
148
151
  end
149
152
 
150
153
  block_call = 0
151
154
  ret_val = nil
152
155
 
156
+ response_message = cmd_client_output.b
153
157
  pp [ :debug_imap_command, imap_command_message, cmd_id, cmd_args ] if $DEBUG
154
- @decoder.__send__(cmd_id, tag, *cmd_args) {|responses|
158
+ @decoder.__send__(cmd_id, tag, *cmd_args, **cmd_kw_args) {|response|
155
159
  block_call += 1
156
- response_message = cmd_client_output.b
157
- if (output) then
160
+ if (block_call == 1 && output) then
158
161
  response_message << output.string
159
162
  end
160
- for response in responses
163
+ if (response != :flush) then
161
164
  response_message << response
162
165
  end
163
- response_lines = StringIO.new(response_message, 'r').each_line
164
- ret_val = yield(response_lines)
165
- assert_raise(StopIteration) { response_lines.next }
166
166
  }
167
+ response_lines = StringIO.new(response_message, 'r').each_line
168
+ ret_val = yield(response_lines)
169
+ assert_raise(StopIteration) { response_lines.next }
167
170
  if (client_input_text) then
168
171
  pp input.string, output.string if $DEBUG
169
172
  end
170
- assert_equal(1, block_call, 'IMAP command block should be called only once.')
171
173
 
172
174
  @decoder = @decoder.next_decoder
173
175
 
@@ -242,7 +244,7 @@ module RIMS::Test
242
244
  extend Forwardable
243
245
 
244
246
  def open_mail_store
245
- @services.call_service(:engine, @unique_user_id) {|engine|
247
+ @drb_services.call_service(:engine, @unique_user_id) {|engine|
246
248
  begin
247
249
  @mail_store = engine.mail_store
248
250
  @mail_store.write_synchronize{
@@ -255,8 +257,15 @@ module RIMS::Test
255
257
  end
256
258
  private :open_mail_store
257
259
 
260
+ LINE_LENGTH_LIMIT = 128
261
+ LITERAL_SIZE_LIMIT = 1024**2
262
+ COMMAND_SIZE_LIMIT = LITERAL_SIZE_LIMIT + LINE_LENGTH_LIMIT
263
+
258
264
  def make_decoder
259
- RIMS::Protocol::Decoder.new_decoder(@services, @auth, @logger)
265
+ RIMS::Protocol::Decoder.new_decoder(@drb_services, @auth, @logger,
266
+ line_length_limit: LINE_LENGTH_LIMIT,
267
+ literal_size_limit: LITERAL_SIZE_LIMIT,
268
+ command_size_limit: COMMAND_SIZE_LIMIT)
260
269
  end
261
270
  private :make_decoder
262
271
 
@@ -288,20 +297,26 @@ module RIMS::Test
288
297
  }
289
298
  @unique_user_id = RIMS::Authentication.unique_user_id('foo')
290
299
 
291
- @services = Riser::DRbServices.new(0)
292
- @services.add_sticky_process_service(:engine,
293
- Riser::ResourceSet.build{|builder|
294
- builder.at_create{|unique_user_id|
295
- mail_store = RIMS::MailStore.build(unique_user_id, @kvs_open, @kvs_open)
296
- RIMS::Protocol::Decoder::Engine.new(unique_user_id, mail_store, @logger)
297
- }
298
- builder.at_destroy{|engine|
299
- engine.destroy
300
- }
301
- builder.alias_unref(:destroy)
302
- })
303
- @services.start_server
304
- @services.start_client
300
+ @bulk_response_count = 10
301
+ @charset_aliases = RIMS::RFC822::CharsetAliases.new
302
+ @charset_convert_options = {}
303
+ @drb_services = Riser::DRbServices.new(0)
304
+ @drb_services.add_sticky_process_service(:engine,
305
+ Riser::ResourceSet.build{|builder|
306
+ builder.at_create{|unique_user_id|
307
+ mail_store = RIMS::MailStore.build(unique_user_id, @kvs_open, @kvs_open)
308
+ RIMS::Protocol::Decoder::Engine.new(unique_user_id, mail_store, @logger,
309
+ bulk_response_count: @bulk_response_count,
310
+ charset_aliases: @charset_aliases,
311
+ charset_convert_options: @charset_convert_options)
312
+ }
313
+ builder.at_destroy{|engine|
314
+ engine.destroy
315
+ }
316
+ builder.alias_unref(:destroy)
317
+ })
318
+ @drb_services.start_server
319
+ @drb_services.start_client
305
320
 
306
321
  open_mail_store{
307
322
  @inbox_id = @mail_store.mbox_id('INBOX')
@@ -328,8 +343,8 @@ module RIMS::Test
328
343
  end
329
344
 
330
345
  def teardown
331
- assert_equal(0, @services.get_service(:engine, @unique_user_id).proxy_count)
332
- @services.stop_server
346
+ assert_equal(0, @drb_services.get_service(:engine, @unique_user_id).proxy_count)
347
+ @drb_services.stop_server
333
348
  pp @kvs if $DEBUG
334
349
  end
335
350
 
@@ -372,6 +387,17 @@ module RIMS::Test
372
387
  end
373
388
  private :assert_imap_command
374
389
 
390
+ def assert_imap_closed
391
+ if (stream_test?) then
392
+ assert_raise(Errno::EPIPE, StopIteration) {
393
+ assert_imap_command('NOOP') {|assert|
394
+ assert.equal("#{tag} OK NOOP completed")
395
+ }
396
+ }
397
+ end
398
+ end
399
+ private :assert_imap_closed
400
+
375
401
  def client_plain_response_base64(authentication_id, plain_password)
376
402
  response_txt = [ authentication_id, authentication_id, plain_password ].join("\0")
377
403
  RIMS::Protocol.encode_base64(response_txt)
@@ -554,6 +580,8 @@ module RIMS::Test
554
580
  assert.match(/^\* BYE /)
555
581
  assert.equal("#{tag} OK LOGOUT completed")
556
582
  }
583
+
584
+ assert_imap_closed
557
585
  }
558
586
  end
559
587
 
@@ -599,6 +627,8 @@ module RIMS::Test
599
627
  }
600
628
 
601
629
  assert_equal(false, @decoder.auth?) if command_test?
630
+
631
+ assert_imap_closed
602
632
  }
603
633
  end
604
634
 
@@ -650,6 +680,8 @@ module RIMS::Test
650
680
  }
651
681
 
652
682
  assert_equal(false, @decoder.auth?) if command_test?
683
+
684
+ assert_imap_closed
653
685
  }
654
686
  end
655
687
 
@@ -705,6 +737,8 @@ module RIMS::Test
705
737
  }
706
738
 
707
739
  assert_equal(false, @decoder.auth?) if command_test?
740
+
741
+ assert_imap_closed
708
742
  }
709
743
  end
710
744
 
@@ -747,6 +781,8 @@ module RIMS::Test
747
781
  }
748
782
 
749
783
  assert_equal(false, @decoder.auth?) if command_test?
784
+
785
+ assert_imap_closed
750
786
  }
751
787
  end
752
788
 
@@ -828,6 +864,8 @@ module RIMS::Test
828
864
  assert_equal(false, @decoder.selected?)
829
865
  end
830
866
 
867
+ assert_imap_closed
868
+
831
869
  open_mail_store{
832
870
  assert_msg_uid( 2, 3)
833
871
  assert_flag_enabled_msgs('answered', )
@@ -878,6 +916,8 @@ module RIMS::Test
878
916
  assert.match(/^\* BYE /)
879
917
  assert.equal("#{tag} OK LOGOUT completed")
880
918
  }
919
+
920
+ assert_imap_closed
881
921
  }
882
922
  end
883
923
 
@@ -959,6 +999,8 @@ module RIMS::Test
959
999
  assert_equal(false, @decoder.selected?)
960
1000
  end
961
1001
 
1002
+ assert_imap_closed
1003
+
962
1004
  open_mail_store{
963
1005
  assert_msg_uid( 1, 2, 3)
964
1006
  assert_flag_enabled_msgs('answered', )
@@ -1009,6 +1051,8 @@ module RIMS::Test
1009
1051
  assert.match(/^\* BYE /)
1010
1052
  assert.equal("#{tag} OK LOGOUT completed")
1011
1053
  }
1054
+
1055
+ assert_imap_closed
1012
1056
  }
1013
1057
  end
1014
1058
 
@@ -1058,6 +1102,8 @@ module RIMS::Test
1058
1102
  assert.match(/^\* BYE /)
1059
1103
  assert.equal("#{tag} OK LOGOUT completed")
1060
1104
  }
1105
+
1106
+ assert_imap_closed
1061
1107
  }
1062
1108
  end
1063
1109
 
@@ -1094,6 +1140,8 @@ module RIMS::Test
1094
1140
  assert.match(/^\* BYE /)
1095
1141
  assert.equal("#{tag} OK LOGOUT completed")
1096
1142
  }
1143
+
1144
+ assert_imap_closed
1097
1145
  }
1098
1146
  end
1099
1147
 
@@ -1166,6 +1214,8 @@ module RIMS::Test
1166
1214
  assert.match(/^\* BYE /)
1167
1215
  assert.equal("#{tag} OK LOGOUT completed")
1168
1216
  }
1217
+
1218
+ assert_imap_closed
1169
1219
  }
1170
1220
  end
1171
1221
 
@@ -1206,6 +1256,8 @@ module RIMS::Test
1206
1256
  assert.match(/^\* BYE /)
1207
1257
  assert.equal("#{tag} OK LOGOUT completed")
1208
1258
  }
1259
+
1260
+ assert_imap_closed
1209
1261
  }
1210
1262
  end
1211
1263
 
@@ -1285,6 +1337,8 @@ module RIMS::Test
1285
1337
  assert.match(/^\* BYE /)
1286
1338
  assert.equal("#{tag} OK LOGOUT completed")
1287
1339
  }
1340
+
1341
+ assert_imap_closed
1288
1342
  }
1289
1343
  end
1290
1344
 
@@ -1332,6 +1386,8 @@ module RIMS::Test
1332
1386
  assert.match(/^\* BYE /)
1333
1387
  assert.equal("#{tag} OK LOGOUT completed")
1334
1388
  }
1389
+
1390
+ assert_imap_closed
1335
1391
  }
1336
1392
  end
1337
1393
 
@@ -1401,6 +1457,8 @@ module RIMS::Test
1401
1457
  assert.match(/^\* BYE /)
1402
1458
  assert.equal("#{tag} OK LOGOUT completed")
1403
1459
  }
1460
+
1461
+ assert_imap_closed
1404
1462
  }
1405
1463
  end
1406
1464
 
@@ -1511,6 +1569,8 @@ module RIMS::Test
1511
1569
  assert.match(/^\* BYE /)
1512
1570
  assert.equal("#{tag} OK LOGOUT completed")
1513
1571
  }
1572
+
1573
+ assert_imap_closed
1514
1574
  }
1515
1575
  end
1516
1576
 
@@ -1549,6 +1609,8 @@ module RIMS::Test
1549
1609
  assert.match(/^\* BYE /)
1550
1610
  assert.equal("#{tag} OK LOGOUT completed")
1551
1611
  }
1612
+
1613
+ assert_imap_closed
1552
1614
  }
1553
1615
  end
1554
1616
 
@@ -1655,6 +1717,8 @@ module RIMS::Test
1655
1717
  assert.match(/^\* BYE /)
1656
1718
  assert.equal("#{tag} OK LOGOUT completed")
1657
1719
  }
1720
+
1721
+ assert_imap_closed
1658
1722
  }
1659
1723
  end
1660
1724
 
@@ -1686,6 +1750,8 @@ module RIMS::Test
1686
1750
  assert.match(/^\* BYE /)
1687
1751
  assert.equal("#{tag} OK LOGOUT completed")
1688
1752
  }
1753
+
1754
+ assert_imap_closed
1689
1755
  }
1690
1756
  end
1691
1757
 
@@ -1753,6 +1819,8 @@ module RIMS::Test
1753
1819
  assert.match(/^\* BYE /)
1754
1820
  assert.equal("#{tag} OK LOGOUT completed")
1755
1821
  }
1822
+
1823
+ assert_imap_closed
1756
1824
  }
1757
1825
  end
1758
1826
 
@@ -1873,6 +1941,8 @@ module RIMS::Test
1873
1941
  assert.match(/^\* BYE /)
1874
1942
  assert.equal("#{tag} OK LOGOUT completed")
1875
1943
  }
1944
+
1945
+ assert_imap_closed
1876
1946
  }
1877
1947
  end
1878
1948
 
@@ -1912,6 +1982,8 @@ module RIMS::Test
1912
1982
  assert.match(/^\* BYE /)
1913
1983
  assert.equal("#{tag} OK LOGOUT completed")
1914
1984
  }
1985
+
1986
+ assert_imap_closed
1915
1987
  }
1916
1988
  end
1917
1989
 
@@ -1920,6 +1992,69 @@ module RIMS::Test
1920
1992
  test_append_utf7_mbox_name
1921
1993
  end
1922
1994
 
1995
+ def test_append_literal_stream
1996
+ make_mail_simple
1997
+ make_mail_multipart
1998
+ make_mail_mime_subject
1999
+
2000
+ use_imap_stream_decode_engine # always run in stream
2001
+ imap_decode_engine_evaluate{
2002
+ if (stream_test?) then
2003
+ assert_untagged_response{|assert|
2004
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
2005
+ }
2006
+ end
2007
+
2008
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
2009
+ assert.equal("#{tag} OK LOGIN completed")
2010
+ }
2011
+
2012
+ open_mail_store{
2013
+ assert_msg_uid()
2014
+ }
2015
+
2016
+ assert_imap_command("APPEND INBOX #{literal(@simple_mail.raw_source)}") {|assert|
2017
+ assert.match(/^\+ /)
2018
+ assert.match(/^#{tag} OK \[APPENDUID \d+ \d+\] APPEND completed/)
2019
+ }
2020
+
2021
+ open_mail_store{
2022
+ assert_msg_uid(1)
2023
+ assert_equal(@simple_mail.raw_source, get_msg_text(1))
2024
+ assert_msg_flags(1, recent: true)
2025
+ }
2026
+
2027
+ assert_imap_command("APPEND INBOX #{literal(@mpart_mail.raw_source)}") {|assert|
2028
+ assert.match(/^\+ /)
2029
+ assert.match(/^#{tag} OK \[APPENDUID \d+ \d+\] APPEND completed/)
2030
+ }
2031
+
2032
+ open_mail_store{
2033
+ assert_msg_uid(1, 2)
2034
+ assert_equal(@mpart_mail.raw_source, get_msg_text(2))
2035
+ assert_msg_flags(2, recent: true)
2036
+ }
2037
+
2038
+ assert_imap_command("APPEND INBOX #{literal(@mime_subject_mail.raw_source)}") {|assert|
2039
+ assert.match(/^\+ /)
2040
+ assert.match(/^#{tag} OK \[APPENDUID \d+ \d+\] APPEND completed/)
2041
+ }
2042
+
2043
+ open_mail_store{
2044
+ assert_msg_uid(1, 2, 3)
2045
+ assert_equal(@mime_subject_mail.raw_source, get_msg_text(3))
2046
+ assert_msg_flags(3, recent: true)
2047
+ }
2048
+
2049
+ assert_imap_command('LOGOUT') {|assert|
2050
+ assert.match(/^\* BYE /)
2051
+ assert.equal("#{tag} OK LOGOUT completed")
2052
+ }
2053
+
2054
+ assert_imap_closed
2055
+ }
2056
+ end
2057
+
1923
2058
  def test_check
1924
2059
  imap_decode_engine_evaluate{
1925
2060
  if (stream_test?) then
@@ -1956,7 +2091,7 @@ module RIMS::Test
1956
2091
  }
1957
2092
 
1958
2093
  assert_imap_command('SELECT INBOX') {|assert|
1959
- assert.skip_while{|line| line =~ /^\* /}
2094
+ assert.skip_while{|line| line =~ /^\* / }
1960
2095
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
1961
2096
  }
1962
2097
 
@@ -1973,6 +2108,8 @@ module RIMS::Test
1973
2108
  assert.match(/^\* BYE /)
1974
2109
  assert.equal("#{tag} OK LOGOUT completed")
1975
2110
  }
2111
+
2112
+ assert_imap_closed
1976
2113
  }
1977
2114
  end
1978
2115
 
@@ -2084,6 +2221,8 @@ module RIMS::Test
2084
2221
  assert_equal(false, @decoder.auth?)
2085
2222
  assert_equal(false, @decoder.selected?)
2086
2223
  end
2224
+
2225
+ assert_imap_closed
2087
2226
  }
2088
2227
  end
2089
2228
 
@@ -2188,6 +2327,8 @@ module RIMS::Test
2188
2327
  assert_equal(false, @decoder.auth?)
2189
2328
  assert_equal(false, @decoder.selected?)
2190
2329
  end
2330
+
2331
+ assert_imap_closed
2191
2332
  }
2192
2333
  end
2193
2334
 
@@ -2338,6 +2479,8 @@ module RIMS::Test
2338
2479
  assert.match(/^\* BYE /)
2339
2480
  assert.equal("#{tag} OK LOGOUT completed")
2340
2481
  }
2482
+
2483
+ assert_imap_closed
2341
2484
  }
2342
2485
  end
2343
2486
 
@@ -2414,6 +2557,8 @@ module RIMS::Test
2414
2557
  assert.match(/^\* BYE /)
2415
2558
  assert.equal("#{tag} OK LOGOUT completed")
2416
2559
  }
2560
+
2561
+ assert_imap_closed
2417
2562
  }
2418
2563
  end
2419
2564
 
@@ -2439,11 +2584,6 @@ module RIMS::Test
2439
2584
  assert.match(/^#{tag} NO /, peek_next_line: true).match(/not auth/)
2440
2585
  }
2441
2586
 
2442
- if (command_test?) then
2443
- assert_equal(false, @decoder.auth?)
2444
- assert_equal(false, @decoder.selected?)
2445
- end
2446
-
2447
2587
  assert_imap_command('LOGIN foo open_sesame') {|assert|
2448
2588
  assert.equal("#{tag} OK LOGIN completed")
2449
2589
  }
@@ -2548,6 +2688,8 @@ module RIMS::Test
2548
2688
  assert.match(/^\* BYE /)
2549
2689
  assert.equal("#{tag} OK LOGOUT completed")
2550
2690
  }
2691
+
2692
+ assert_imap_closed
2551
2693
  }
2552
2694
  end
2553
2695
 
@@ -2591,15 +2733,10 @@ module RIMS::Test
2591
2733
  assert_equal(false, @decoder.selected?)
2592
2734
  end
2593
2735
 
2594
- assert_imap_command('SEARCH CHARSET utf-8 ALL') {|assert|
2736
+ assert_imap_command('SEARCH CHARSET us-ascii ALL') {|assert|
2595
2737
  assert.match(/^#{tag} NO /, peek_next_line: true).match(/not auth/)
2596
2738
  }
2597
2739
 
2598
- if (command_test?) then
2599
- assert_equal(false, @decoder.auth?)
2600
- assert_equal(false, @decoder.selected?)
2601
- end
2602
-
2603
2740
  assert_imap_command('LOGIN foo open_sesame') {|assert|
2604
2741
  assert.equal("#{tag} OK LOGIN completed")
2605
2742
  }
@@ -2609,7 +2746,7 @@ module RIMS::Test
2609
2746
  assert_equal(false, @decoder.selected?)
2610
2747
  end
2611
2748
 
2612
- assert_imap_command('SEARCH CHARSET utf-8 ALL') {|assert|
2749
+ assert_imap_command('SEARCH CHARSET us-ascii ALL') {|assert|
2613
2750
  assert.match(/^#{tag} NO /, peek_next_line: true).match(/not selected/)
2614
2751
  }
2615
2752
 
@@ -2623,28 +2760,37 @@ module RIMS::Test
2623
2760
  assert_equal(true, @decoder.selected?)
2624
2761
  end
2625
2762
 
2626
- assert_imap_command('SEARCH CHARSET utf-8 ALL') {|assert|
2763
+ assert_imap_command('SEARCH CHARSET us-ascii ALL') {|assert|
2627
2764
  assert.equal("* SEARCH 1 2 3 4 5\r\n")
2628
2765
  assert.equal("#{tag} OK SEARCH completed\r\n")
2629
2766
  }
2630
2767
 
2631
- assert_imap_command('SEARCH CHARSET utf-8 BODY foo') {|assert|
2768
+ assert_imap_command('SEARCH CHARSET us-ascii BODY foo') {|assert|
2632
2769
  assert.equal("* SEARCH 1 2 3\r\n")
2633
2770
  assert.equal("#{tag} OK SEARCH completed\r\n")
2634
2771
  }
2635
2772
 
2636
- assert_imap_command('SEARCH CHARSET utf-8 BODY bar') {|assert|
2773
+ assert_imap_command('SEARCH CHARSET us-ascii BODY bar') {|assert|
2637
2774
  assert.equal("* SEARCH\r\n")
2638
2775
  assert.equal("#{tag} OK SEARCH completed\r\n")
2639
2776
  }
2640
2777
 
2641
2778
  utf8_msg = "\u306F\u306B\u307B"
2642
- assert_imap_command("SEARCH CHARSET utf-8 BODY {#{utf8_msg.bytesize}}\r\n#{utf8_msg}".b) {|assert|
2779
+ assert_imap_command("SEARCH CHARSET utf-8 BODY #{literal(utf8_msg)}".b) {|assert|
2643
2780
  assert.match(/^\+ /)
2644
2781
  assert.equal("* SEARCH 4 5\r\n")
2645
2782
  assert.equal("#{tag} OK SEARCH completed\r\n")
2646
2783
  }
2647
2784
 
2785
+ assert_imap_command("SEARCH CHARSET euc-jp BODY #{literal(utf8_msg)}".b) {|assert|
2786
+ assert.match(/^\+ /)
2787
+ assert.match(/^#{tag} BAD /, peek_next_line: true).match(/syntax error/)
2788
+ }
2789
+
2790
+ assert_imap_command('SEARCH CHARSET x-nothing BODY foo') {|assert|
2791
+ assert.match(/^#{tag} NO \[BADCHARSET \(\S+( \S+)*\)\] unknown charset/)
2792
+ }
2793
+
2648
2794
  assert_imap_command('SEARCH CHARSET') {|assert|
2649
2795
  assert.match(/^#{tag} BAD /, peek_next_line: true).match(/syntax error/)
2650
2796
  }
@@ -2661,6 +2807,8 @@ module RIMS::Test
2661
2807
  assert.match(/^\* BYE /)
2662
2808
  assert.equal("#{tag} OK LOGOUT completed")
2663
2809
  }
2810
+
2811
+ assert_imap_closed
2664
2812
  }
2665
2813
  end
2666
2814
 
@@ -2679,6 +2827,7 @@ module RIMS::Test
2679
2827
 
2680
2828
  open_mail_store{
2681
2829
  add_msg("Content-Type: text/plain\r\n" +
2830
+ "\r\n" +
2682
2831
  "foo")
2683
2832
  add_msg("Content-Type: text/plain; charset=utf-8\r\n" +
2684
2833
  "X-foo: dummy\r\n" +
@@ -2705,15 +2854,10 @@ module RIMS::Test
2705
2854
  assert_equal(false, @decoder.selected?)
2706
2855
  end
2707
2856
 
2708
- assert_imap_command('SEARCH CHARSET utf-8 ALL') {|assert|
2857
+ assert_imap_command('SEARCH CHARSET us-ascii ALL') {|assert|
2709
2858
  assert.match(/^#{tag} NO /, peek_next_line: true).match(/not auth/)
2710
2859
  }
2711
2860
 
2712
- if (command_test?) then
2713
- assert_equal(false, @decoder.auth?)
2714
- assert_equal(false, @decoder.selected?)
2715
- end
2716
-
2717
2861
  assert_imap_command('LOGIN foo open_sesame') {|assert|
2718
2862
  assert.equal("#{tag} OK LOGIN completed")
2719
2863
  }
@@ -2723,7 +2867,7 @@ module RIMS::Test
2723
2867
  assert_equal(false, @decoder.selected?)
2724
2868
  end
2725
2869
 
2726
- assert_imap_command('SEARCH CHARSET utf-8 ALL') {|assert|
2870
+ assert_imap_command('SEARCH CHARSET us-ascii ALL') {|assert|
2727
2871
  assert.match(/^#{tag} NO /, peek_next_line: true).match(/not selected/)
2728
2872
  }
2729
2873
 
@@ -2737,12 +2881,12 @@ module RIMS::Test
2737
2881
  assert_equal(true, @decoder.selected?)
2738
2882
  end
2739
2883
 
2740
- assert_imap_command('SEARCH CHARSET utf-8 ALL') {|assert|
2884
+ assert_imap_command('SEARCH CHARSET us-ascii ALL') {|assert|
2741
2885
  assert.equal("* SEARCH 1 2 3 4 5\r\n")
2742
2886
  assert.equal("#{tag} OK SEARCH completed\r\n")
2743
2887
  }
2744
2888
 
2745
- assert_imap_command('SEARCH CHARSET utf-8 TEXT foo') {|assert|
2889
+ assert_imap_command('SEARCH CHARSET us-ascii TEXT foo') {|assert|
2746
2890
  assert.equal("* SEARCH 1 2 3\r\n")
2747
2891
  assert.equal("#{tag} OK SEARCH completed\r\n")
2748
2892
  }
@@ -2752,18 +2896,27 @@ module RIMS::Test
2752
2896
  assert.equal("#{tag} OK SEARCH completed\r\n")
2753
2897
  }
2754
2898
 
2755
- assert_imap_command('SEARCH CHARSET utf-8 TEXT baz') {|assert|
2899
+ assert_imap_command('SEARCH CHARSET us-ascii TEXT baz') {|assert|
2756
2900
  assert.equal("* SEARCH\r\n")
2757
2901
  assert.equal("#{tag} OK SEARCH completed\r\n")
2758
2902
  }
2759
2903
 
2760
2904
  utf8_msg = "\u306F\u306B\u307B"
2761
- assert_imap_command("SEARCH CHARSET utf-8 TEXT {#{utf8_msg.bytesize}}\r\n#{utf8_msg}".b) {|assert|
2905
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(utf8_msg)}".b) {|assert|
2762
2906
  assert.match(/^\+ /)
2763
2907
  assert.equal("* SEARCH 4 5\r\n")
2764
2908
  assert.equal("#{tag} OK SEARCH completed\r\n")
2765
2909
  }
2766
2910
 
2911
+ assert_imap_command("SEARCH CHARSET euc-jp TEXT #{literal(utf8_msg)}".b) {|assert|
2912
+ assert.match(/^\+ /)
2913
+ assert.match(/^#{tag} BAD /, peek_next_line: true).match(/syntax error/)
2914
+ }
2915
+
2916
+ assert_imap_command('SEARCH CHARSET x-nothing TEXT foo') {|assert|
2917
+ assert.match(/^#{tag} NO \[BADCHARSET \(\S+( \S+)*\)\] unknown charset/)
2918
+ }
2919
+
2767
2920
  assert_imap_command('SEARCH CHARSET') {|assert|
2768
2921
  assert.match(/^#{tag} BAD /, peek_next_line: true).match(/syntax error/)
2769
2922
  }
@@ -2780,6 +2933,8 @@ module RIMS::Test
2780
2933
  assert.match(/^\* BYE /)
2781
2934
  assert.equal("#{tag} OK LOGOUT completed")
2782
2935
  }
2936
+
2937
+ assert_imap_closed
2783
2938
  }
2784
2939
  end
2785
2940
 
@@ -2788,6 +2943,146 @@ module RIMS::Test
2788
2943
  test_search_charset_text
2789
2944
  end
2790
2945
 
2946
+ def test_search_charset_skip_encoding_error
2947
+ imap_decode_engine_evaluate{
2948
+ if (stream_test?) then
2949
+ assert_untagged_response{|assert|
2950
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
2951
+ }
2952
+ end
2953
+
2954
+ open_mail_store{
2955
+ add_msg("Content-Type: text/plain\r\n" +
2956
+ "\r\n" +
2957
+ "foo")
2958
+ add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
2959
+ "\r\n" +
2960
+ # utf-8
2961
+ "\u3053\u3093\u306B\u3061\u306F\r\n" +
2962
+ "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\r\n" +
2963
+ "\u3042\u3044\u3046\u3048\u304A\r\n" +
2964
+ "foo\r\n")
2965
+
2966
+ assert_msg_uid(1, 2)
2967
+ }
2968
+
2969
+ if (command_test?) then
2970
+ assert_equal(false, @decoder.auth?)
2971
+ assert_equal(false, @decoder.selected?)
2972
+ end
2973
+
2974
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
2975
+ assert.equal("#{tag} OK LOGIN completed")
2976
+ }
2977
+
2978
+ if (command_test?) then
2979
+ assert_equal(true, @decoder.auth?)
2980
+ assert_equal(false, @decoder.selected?)
2981
+ end
2982
+
2983
+ assert_imap_command('SELECT INBOX') {|assert|
2984
+ assert.skip_while{|line| line =~ /^\* / }
2985
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
2986
+ }
2987
+
2988
+ if (command_test?) then
2989
+ assert_equal(true, @decoder.auth?)
2990
+ assert_equal(true, @decoder.selected?)
2991
+ end
2992
+
2993
+ assert_imap_command('SEARCH CHARSET us-ascii BODY foo') {|assert|
2994
+ assert.equal("* SEARCH 1\r\n")
2995
+ assert.equal("#{tag} OK SEARCH completed\r\n")
2996
+ }
2997
+
2998
+ utf8_msg = "\u306F\u306B\u307B"
2999
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(utf8_msg)}".b) {|assert|
3000
+ assert.match(/^\+ /)
3001
+ assert.equal("* SEARCH\r\n")
3002
+ assert.equal("#{tag} OK SEARCH completed\r\n")
3003
+ }
3004
+
3005
+ assert_imap_command('LOGOUT') {|assert|
3006
+ assert.match(/^\* BYE /)
3007
+ assert.equal("#{tag} OK LOGOUT completed")
3008
+ }
3009
+
3010
+ assert_imap_closed
3011
+ }
3012
+ end
3013
+
3014
+ def test_search_charset_skip_encoding_error_stream
3015
+ use_imap_stream_decode_engine
3016
+ test_search_charset_skip_encoding_error
3017
+ end
3018
+
3019
+ # skip this test because `EncodingError' is ignored.
3020
+ def _test_search_interrupt_by_bad_response
3021
+ imap_decode_engine_evaluate{
3022
+ if (stream_test?) then
3023
+ assert_untagged_response{|assert|
3024
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
3025
+ }
3026
+ end
3027
+
3028
+ open_mail_store{
3029
+ @bulk_response_count.times do
3030
+ add_msg("Content-Type: text/plain\r\n" +
3031
+ "\r\n" +
3032
+ "foo")
3033
+ end
3034
+
3035
+ add_msg("Content-Type: text/plain; charset=x-nothing\r\n" +
3036
+ "\r\n" +
3037
+ "foo")
3038
+
3039
+ assert_msg_uid(*(1..(@bulk_response_count.succ)).to_a)
3040
+ }
3041
+
3042
+ if (command_test?) then
3043
+ assert_equal(false, @decoder.auth?)
3044
+ assert_equal(false, @decoder.selected?)
3045
+ end
3046
+
3047
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
3048
+ assert.equal("#{tag} OK LOGIN completed")
3049
+ }
3050
+
3051
+ if (command_test?) then
3052
+ assert_equal(true, @decoder.auth?)
3053
+ assert_equal(false, @decoder.selected?)
3054
+ end
3055
+
3056
+ assert_imap_command('SELECT INBOX') {|assert|
3057
+ assert.skip_while{|line| line =~ /^\* / }
3058
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
3059
+ }
3060
+
3061
+ if (command_test?) then
3062
+ assert_equal(true, @decoder.auth?)
3063
+ assert_equal(true, @decoder.selected?)
3064
+ end
3065
+
3066
+ assert_imap_command('SEARCH CHARSET utf-8 TEXT foo') {|assert|
3067
+ assert.match(/\A\* SEARCH( \d+)+\r\n\z/, peek_next_line: true).no_match(/ #{@bulk_response_count.succ}/)
3068
+ assert.equal("#{tag} BAD internal server error\r\n")
3069
+ }
3070
+
3071
+ assert_imap_command('LOGOUT') {|assert|
3072
+ assert.match(/^\* BYE /)
3073
+ assert.equal("#{tag} OK LOGOUT completed")
3074
+ }
3075
+
3076
+ assert_imap_closed
3077
+ }
3078
+ end
3079
+
3080
+ # skip this test because `EncodingError' is ignored.
3081
+ def _test_search_interrupt_by_bad_response_stream
3082
+ use_imap_stream_decode_engine
3083
+ test_search_interrupt_by_bad_response
3084
+ end
3085
+
2791
3086
  def test_fetch
2792
3087
  imap_decode_engine_evaluate{
2793
3088
  if (stream_test?) then
@@ -2932,6 +3227,8 @@ module RIMS::Test
2932
3227
  assert.match(/^\* BYE /)
2933
3228
  assert.equal("#{tag} OK LOGOUT completed")
2934
3229
  }
3230
+
3231
+ assert_imap_closed
2935
3232
  }
2936
3233
  end
2937
3234
 
@@ -3084,6 +3381,8 @@ module RIMS::Test
3084
3381
  assert.match(/^\* BYE /)
3085
3382
  assert.equal("#{tag} OK LOGOUT completed")
3086
3383
  }
3384
+
3385
+ assert_imap_closed
3087
3386
  }
3088
3387
  end
3089
3388
 
@@ -3436,6 +3735,8 @@ module RIMS::Test
3436
3735
  assert.match(/^\* BYE /)
3437
3736
  assert.equal("#{tag} OK LOGOUT completed")
3438
3737
  }
3738
+
3739
+ assert_imap_closed
3439
3740
  }
3440
3741
  end
3441
3742
 
@@ -3738,6 +4039,8 @@ module RIMS::Test
3738
4039
  assert.match(/^\* BYE /)
3739
4040
  assert.equal("#{tag} OK LOGOUT completed")
3740
4041
  }
4042
+
4043
+ assert_imap_closed
3741
4044
  }
3742
4045
  end
3743
4046
 
@@ -4090,6 +4393,8 @@ module RIMS::Test
4090
4393
  assert.match(/^\* BYE /)
4091
4394
  assert.equal("#{tag} OK LOGOUT completed")
4092
4395
  }
4396
+
4397
+ assert_imap_closed
4093
4398
  }
4094
4399
  end
4095
4400
 
@@ -4392,6 +4697,8 @@ module RIMS::Test
4392
4697
  assert.match(/^\* BYE /)
4393
4698
  assert.equal("#{tag} OK LOGOUT completed")
4394
4699
  }
4700
+
4701
+ assert_imap_closed
4395
4702
  }
4396
4703
  end
4397
4704
 
@@ -4561,6 +4868,8 @@ module RIMS::Test
4561
4868
  assert.match(/^\* BYE /)
4562
4869
  assert.equal("#{tag} OK LOGOUT completed")
4563
4870
  }
4871
+
4872
+ assert_imap_closed
4564
4873
  }
4565
4874
  end
4566
4875
 
@@ -4765,6 +5074,8 @@ module RIMS::Test
4765
5074
  assert.match(/^\* BYE /)
4766
5075
  assert.equal("#{tag} OK LOGOUT completed")
4767
5076
  }
5077
+
5078
+ assert_imap_closed
4768
5079
  }
4769
5080
  end
4770
5081
 
@@ -4969,6 +5280,8 @@ module RIMS::Test
4969
5280
  assert.match(/^\* BYE /)
4970
5281
  assert.equal("#{tag} OK LOGOUT completed")
4971
5282
  }
5283
+
5284
+ assert_imap_closed
4972
5285
  }
4973
5286
  end
4974
5287
 
@@ -5022,6 +5335,8 @@ module RIMS::Test
5022
5335
  assert.match(/^\* BYE /)
5023
5336
  assert.equal("#{tag} OK LOGOUT completed")
5024
5337
  }
5338
+
5339
+ assert_imap_closed
5025
5340
  }
5026
5341
  end
5027
5342
 
@@ -5065,7 +5380,7 @@ module RIMS::Test
5065
5380
  }
5066
5381
 
5067
5382
  assert_imap_command('SELECT INBOX') {|assert|
5068
- assert.skip_while{|line| line =~ /^\* /}
5383
+ assert.skip_while{|line| line =~ /^\* / }
5069
5384
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
5070
5385
  }
5071
5386
 
@@ -5092,7 +5407,7 @@ module RIMS::Test
5092
5407
  }
5093
5408
 
5094
5409
  assert_imap_command('EXAMINE INBOX') {|assert|
5095
- assert.skip_while{|line| line =~ /^\* /}
5410
+ assert.skip_while{|line| line =~ /^\* / }
5096
5411
  assert.equal("#{tag} OK [READ-ONLY] EXAMINE completed")
5097
5412
  }
5098
5413
 
@@ -5114,6 +5429,8 @@ module RIMS::Test
5114
5429
  assert_equal(false, @decoder.auth?)
5115
5430
  assert_equal(false, @decoder.selected?)
5116
5431
  end
5432
+
5433
+ assert_imap_closed
5117
5434
  }
5118
5435
  end
5119
5436
 
@@ -5153,7 +5470,7 @@ module RIMS::Test
5153
5470
  }
5154
5471
 
5155
5472
  assert_imap_command('SELECT INBOX') {|assert|
5156
- assert.skip_while{|line| line =~ /^\* /}
5473
+ assert.skip_while{|line| line =~ /^\* / }
5157
5474
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
5158
5475
  }
5159
5476
 
@@ -5199,7 +5516,7 @@ module RIMS::Test
5199
5516
  }
5200
5517
 
5201
5518
  assert_imap_command('EXAMINE INBOX') {|assert|
5202
- assert.skip_while{|line| line =~ /^\* /}
5519
+ assert.skip_while{|line| line =~ /^\* / }
5203
5520
  assert.equal("#{tag} OK [READ-ONLY] EXAMINE completed")
5204
5521
  }
5205
5522
 
@@ -5222,6 +5539,8 @@ module RIMS::Test
5222
5539
  assert_equal(false, @decoder.auth?)
5223
5540
  assert_equal(false, @decoder.selected?)
5224
5541
  end
5542
+
5543
+ assert_imap_closed
5225
5544
  }
5226
5545
  end
5227
5546
 
@@ -5231,11 +5550,11 @@ module RIMS::Test
5231
5550
  end
5232
5551
 
5233
5552
  def test_error_handling_stream
5234
- use_imap_stream_decode_engine
5553
+ use_imap_stream_decode_engine # always run in stream
5235
5554
  imap_decode_engine_evaluate{
5236
5555
  assert_imap_command('') {|assert|
5237
5556
  assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
5238
- assert.equal('* BAD client command syntax error')
5557
+ assert.equal("#{tag} BAD client command syntax error")
5239
5558
  }
5240
5559
 
5241
5560
  assert_imap_command('no_command') {|assert|
@@ -5253,6 +5572,30 @@ module RIMS::Test
5253
5572
  assert_imap_command('noop detarame') {|assert|
5254
5573
  assert.equal("#{tag} BAD invalid command parameter")
5255
5574
  }
5575
+
5576
+ assert_imap_command("APPEND INBOX {#{LITERAL_SIZE_LIMIT + 1}}") {|assert|
5577
+ assert.equal("#{tag} BAD literal size too large")
5578
+ }
5579
+
5580
+ assert_imap_command("APPEND #{literal('x' * LITERAL_SIZE_LIMIT)} #{'y' * (LINE_LENGTH_LIMIT - 3)}") {|assert|
5581
+ assert.match(/^\+ /)
5582
+ assert.equal("#{tag} BAD command size too large") # by last line
5583
+ }
5584
+
5585
+ assert_imap_command("APPEND #{literal('x' * LITERAL_SIZE_LIMIT)} {#{LITERAL_SIZE_LIMIT}}") {|assert|
5586
+ assert.match(/^\+ /)
5587
+ assert.equal("#{tag} BAD command size too large") # by last literal
5588
+ }
5589
+
5590
+ @tag = '*T000'
5591
+ assert_imap_command('noop') {|assert|
5592
+ assert.equal('* BAD client command syntax error')
5593
+ }
5594
+
5595
+ @tag = '+T000'
5596
+ assert_imap_command('noop') {|assert|
5597
+ assert.equal('* BAD client command syntax error')
5598
+ }
5256
5599
  }
5257
5600
  end
5258
5601
 
@@ -5284,6 +5627,8 @@ module RIMS::Test
5284
5627
  }
5285
5628
 
5286
5629
  assert_equal(false, @decoder.auth?) if command_test?
5630
+
5631
+ assert_imap_closed
5287
5632
  }
5288
5633
  end
5289
5634
 
@@ -5440,6 +5785,8 @@ module RIMS::Test
5440
5785
  }
5441
5786
 
5442
5787
  assert_equal(false, @decoder.auth?) if command_test?
5788
+
5789
+ assert_imap_closed
5443
5790
  }
5444
5791
  end
5445
5792
 
@@ -5498,6 +5845,8 @@ module RIMS::Test
5498
5845
  }
5499
5846
 
5500
5847
  assert_equal(false, @decoder.auth?) if command_test?
5848
+
5849
+ assert_imap_closed
5501
5850
  }
5502
5851
  end
5503
5852
 
@@ -5580,7 +5929,7 @@ module RIMS::Test
5580
5929
  }
5581
5930
 
5582
5931
  assert_imap_command('SELECT INBOX') {|assert|
5583
- assert.skip_while{|line| line =~ /^\* /}
5932
+ assert.skip_while{|line| line =~ /^\* / }
5584
5933
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
5585
5934
  }
5586
5935
 
@@ -5604,7 +5953,7 @@ module RIMS::Test
5604
5953
  }
5605
5954
 
5606
5955
  assert_imap_command('SELECT INBOX') {|assert|
5607
- assert.skip_while{|line| line =~ /^\* /}
5956
+ assert.skip_while{|line| line =~ /^\* / }
5608
5957
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
5609
5958
  }
5610
5959
 
@@ -5628,7 +5977,7 @@ module RIMS::Test
5628
5977
  }
5629
5978
 
5630
5979
  assert_imap_command('SELECT INBOX') {|assert|
5631
- assert.skip_while{|line| line =~ /^\* /}
5980
+ assert.skip_while{|line| line =~ /^\* / }
5632
5981
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
5633
5982
  }
5634
5983
 
@@ -5656,7 +6005,7 @@ module RIMS::Test
5656
6005
  }
5657
6006
 
5658
6007
  assert_imap_command('SELECT INBOX') {|assert|
5659
- assert.skip_while{|line| line =~ /^\* /}
6008
+ assert.skip_while{|line| line =~ /^\* / }
5660
6009
  assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
5661
6010
  }
5662
6011
 
@@ -5689,10 +6038,8 @@ module RIMS::Test
5689
6038
  }
5690
6039
 
5691
6040
  another_decoder = make_decoder
5692
- another_writer = proc{|res|
5693
- for line in res
5694
- p line if $DEBUG
5695
- end
6041
+ another_writer = proc{|response|
6042
+ p response if $DEBUG
5696
6043
  }
5697
6044
 
5698
6045
  another_decoder.login('tag', 'foo', 'open_sesame', &another_writer)
@@ -5967,10 +6314,8 @@ module RIMS::Test
5967
6314
  }
5968
6315
 
5969
6316
  another_decoder = make_decoder
5970
- another_writer = proc{|res|
5971
- for line in res
5972
- p line if $DEBUG
5973
- end
6317
+ another_writer = proc{|response|
6318
+ p response if $DEBUG
5974
6319
  }
5975
6320
 
5976
6321
  another_decoder.login('tag', 'foo', 'open_sesame', &another_writer)
@@ -6031,6 +6376,232 @@ module RIMS::Test
6031
6376
  use_imap_stream_decode_engine
6032
6377
  test_idle_untagged_server_response
6033
6378
  end
6379
+
6380
+ def test_charset_aliases
6381
+ imap_decode_engine_evaluate{
6382
+ if (stream_test?) then
6383
+ assert_untagged_response{|assert|
6384
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6385
+ }
6386
+ end
6387
+
6388
+ platform_dependent_character = "\u2460"
6389
+ open_mail_store{
6390
+ add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
6391
+ "\r\n" +
6392
+ "#{platform_dependent_character.encode(Encoding::CP50221).b}")
6393
+ }
6394
+
6395
+ if (command_test?) then
6396
+ assert_equal(false, @decoder.auth?)
6397
+ assert_equal(false, @decoder.selected?)
6398
+ end
6399
+
6400
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
6401
+ assert.equal("#{tag} OK LOGIN completed")
6402
+ }
6403
+
6404
+ if (command_test?) then
6405
+ assert_equal(true, @decoder.auth?)
6406
+ assert_equal(false, @decoder.selected?)
6407
+ end
6408
+
6409
+ assert_imap_command('SELECT INBOX') {|assert|
6410
+ assert.skip_while{|line| line =~ /^\* / }
6411
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
6412
+ }
6413
+
6414
+ if (command_test?) then
6415
+ assert_equal(true, @decoder.auth?)
6416
+ assert_equal(true, @decoder.selected?)
6417
+ end
6418
+
6419
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(platform_dependent_character)}") {|assert|
6420
+ assert.match(/^\+ /)
6421
+ assert.equal("* SEARCH\r\n") # skip by `EncodingError'
6422
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6423
+ }
6424
+
6425
+ # not recommend changing charset aliases after passing them to the decoder.
6426
+ # here, the charset aliases are reluctantly changed for testing.
6427
+ @charset_aliases.add_alias('iso-2022-jp', Encoding::CP50221)
6428
+
6429
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(platform_dependent_character)}") {|assert|
6430
+ assert.match(/^\+ /)
6431
+ assert.equal("* SEARCH 1\r\n") # matched!
6432
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6433
+ }
6434
+
6435
+ assert_imap_command('CLOSE') {|assert|
6436
+ assert.equal("#{tag} OK CLOSE completed")
6437
+ }
6438
+
6439
+ if (command_test?) then
6440
+ assert_equal(true, @decoder.auth?)
6441
+ assert_equal(false, @decoder.selected?)
6442
+ end
6443
+
6444
+ assert_imap_command('LOGOUT') {|assert|
6445
+ assert.match(/^\* BYE /)
6446
+ assert.equal("#{tag} OK LOGOUT completed")
6447
+ }
6448
+
6449
+ if (command_test?) then
6450
+ assert_equal(false, @decoder.auth?)
6451
+ assert_equal(false, @decoder.selected?)
6452
+ end
6453
+
6454
+ assert_imap_closed
6455
+ }
6456
+ end
6457
+
6458
+ def test_charset_aliases_stream
6459
+ use_imap_stream_decode_engine
6460
+ test_charset_aliases
6461
+ end
6462
+
6463
+ def test_charset_convert_options
6464
+ imap_decode_engine_evaluate{
6465
+ if (stream_test?) then
6466
+ assert_untagged_response{|assert|
6467
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6468
+ }
6469
+ end
6470
+
6471
+ platform_dependent_character = "\u2460"
6472
+ open_mail_store{
6473
+ add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
6474
+ "\r\n" +
6475
+ "#{platform_dependent_character.encode(Encoding::CP50221).b}")
6476
+ }
6477
+
6478
+ if (command_test?) then
6479
+ assert_equal(false, @decoder.auth?)
6480
+ assert_equal(false, @decoder.selected?)
6481
+ end
6482
+
6483
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
6484
+ assert.equal("#{tag} OK LOGIN completed")
6485
+ }
6486
+
6487
+ if (command_test?) then
6488
+ assert_equal(true, @decoder.auth?)
6489
+ assert_equal(false, @decoder.selected?)
6490
+ end
6491
+
6492
+ assert_imap_command('SELECT INBOX') {|assert|
6493
+ assert.skip_while{|line| line =~ /^\* / }
6494
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
6495
+ }
6496
+
6497
+ if (command_test?) then
6498
+ assert_equal(true, @decoder.auth?)
6499
+ assert_equal(true, @decoder.selected?)
6500
+ end
6501
+
6502
+ assert_imap_command(%Q'SEARCH CHARSET utf-8 TEXT #{literal("\uFFFD")}') {|assert|
6503
+ assert.match(/^\+ /)
6504
+ assert.equal("* SEARCH\r\n") # skip by `EncodingError'
6505
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6506
+ }
6507
+
6508
+ # not recommend changing charset convert options after passing them to the decoder.
6509
+ # here, the options are reluctantly changed for testing.
6510
+ @charset_convert_options[:undef] = :replace
6511
+
6512
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal("\uFFFD")}") {|assert|
6513
+ assert.match(/^\+ /)
6514
+ assert.equal("* SEARCH 1\r\n") # matched!
6515
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6516
+ }
6517
+
6518
+ assert_imap_command('CLOSE') {|assert|
6519
+ assert.equal("#{tag} OK CLOSE completed")
6520
+ }
6521
+
6522
+ if (command_test?) then
6523
+ assert_equal(true, @decoder.auth?)
6524
+ assert_equal(false, @decoder.selected?)
6525
+ end
6526
+
6527
+ assert_imap_command('LOGOUT') {|assert|
6528
+ assert.match(/^\* BYE /)
6529
+ assert.equal("#{tag} OK LOGOUT completed")
6530
+ }
6531
+
6532
+ if (command_test?) then
6533
+ assert_equal(false, @decoder.auth?)
6534
+ assert_equal(false, @decoder.selected?)
6535
+ end
6536
+
6537
+ assert_imap_closed
6538
+ }
6539
+ end
6540
+
6541
+ def test_charset_convert_options_stream
6542
+ use_imap_stream_decode_engine
6543
+ test_charset_convert_options
6544
+ end
6545
+
6546
+ def test_command_line_too_long_error_stream
6547
+ use_imap_stream_decode_engine # always run in stream
6548
+ imap_decode_engine_evaluate{
6549
+ assert_untagged_response{|assert|
6550
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6551
+ }
6552
+
6553
+ assert_imap_command('APPEND INBOX ' + 'x' * LINE_LENGTH_LIMIT) {|assert|
6554
+ assert.equal('* BAD line too long')
6555
+ assert.equal('* BYE server autologout: connection terminated')
6556
+ }
6557
+
6558
+ assert_imap_closed
6559
+ }
6560
+ end
6561
+
6562
+ def test_authenticate_line_too_long_error_stream
6563
+ use_imap_stream_decode_engine # always run in stream
6564
+ imap_decode_engine_evaluate{
6565
+ assert_untagged_response{|assert|
6566
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6567
+ }
6568
+
6569
+ assert_imap_command('AUTHENTICATE plain',
6570
+ client_input_text: 'x' * LINE_LENGTH_LIMIT + "\r\n") {|assert|
6571
+ assert.equal('+ ')
6572
+ assert.equal('* BAD line too long')
6573
+ assert.equal('* BYE server autologout: connection terminated')
6574
+ }
6575
+
6576
+ assert_imap_closed
6577
+ }
6578
+ end
6579
+
6580
+ def test_idle_line_too_long_error_stream
6581
+ use_imap_stream_decode_engine # always run in stream
6582
+ imap_decode_engine_evaluate{
6583
+ assert_untagged_response{|assert|
6584
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6585
+ }
6586
+
6587
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
6588
+ assert.equal("#{tag} OK LOGIN completed")
6589
+ }
6590
+
6591
+ assert_imap_command('SELECT INBOX') {|assert|
6592
+ assert.skip_while{|line| line =~ /^\* / }
6593
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
6594
+ }
6595
+
6596
+ assert_imap_command('IDLE', client_input_text: 'DONE' + ' ' * LINE_LENGTH_LIMIT + "\r\n") {|assert|
6597
+ assert.match(/^\+ /)
6598
+ assert.equal('* BAD line too long')
6599
+ assert.equal('* BYE server autologout: connection terminated')
6600
+ }
6601
+
6602
+ assert_imap_closed
6603
+ }
6604
+ end
6034
6605
  end
6035
6606
 
6036
6607
  class ProtocolMailDeliveryDecoderTest < Test::Unit::TestCase
@@ -6053,6 +6624,93 @@ module RIMS::Test
6053
6624
  assert_equal(%w[ foo INBOX ], RIMS::Protocol::Decoder.decode_delivery_target_mailbox(encoded_mbox_name))
6054
6625
  end
6055
6626
  end
6627
+
6628
+ class ProtocolDecoderBulkResponseTest < Test::Unit::TestCase
6629
+ def setup
6630
+ limit_count = 10
6631
+ limit_size = 100
6632
+ @res = RIMS::Protocol::Decoder::BulkResponse.new(limit_count, limit_size)
6633
+ end
6634
+
6635
+ data('empty' => [ 0, 0, [] ],
6636
+ '1' => [ 1, 3, %w[ foo ] ],
6637
+ '2' => [ 2, 6, %w[ foo bar ] ],
6638
+ '3' => [ 3, 11, %w[ foo bar baaaz ] ])
6639
+ def test_add(data)
6640
+ expected_count, expected_size, response_list = data
6641
+
6642
+ assert_equal(0, @res.count)
6643
+ assert_equal(0, @res.size)
6644
+
6645
+ for response in response_list
6646
+ @res << response
6647
+ end
6648
+
6649
+ assert_equal(expected_count, @res.count)
6650
+ assert_equal(expected_size, @res.size)
6651
+ end
6652
+
6653
+ def test_emtpy?
6654
+ assert_true(@res.empty?)
6655
+ end
6656
+
6657
+ def test_not_empty?
6658
+ @res << 'foo'
6659
+ assert_false(@res.empty?)
6660
+ end
6661
+
6662
+ data('count: boundary' => %w[ 1 2 3 4 5 6 7 8 9 0 ],
6663
+ 'count: over' => %w[ 1 2 3 4 5 6 7 8 9 0 a ],
6664
+ 'size: boundary' => [ 'x' * 50, 'y' * 50 ],
6665
+ 'size: over' => [ 'x' * 50, 'y' * 50, 'z' ],
6666
+ 'count,size: boundary' => %w[ 1 2 3 4 5 6 7 8 9 0 ].map{|s| s * 10 },
6667
+ 'count,size: over' => %w[ 1 2 3 4 5 6 7 8 9 0 ].map{|s| s * 10 } + %w[ a ])
6668
+ def test_full?(data)
6669
+ response_list = data
6670
+ for response in response_list
6671
+ @res << response
6672
+ end
6673
+ assert_true(@res.full?)
6674
+ end
6675
+
6676
+ data('empty' => [],
6677
+ 'under' => %w[ foo bar baz ],
6678
+ 'count: boundary' => %w[ 1 2 3 4 5 6 7 8 9 ],
6679
+ 'size: boundary' => [ 'x' * 50, 'y' * 49 ])
6680
+ def test_not_full?(data)
6681
+ response_list = data
6682
+ for response in response_list
6683
+ @res << response
6684
+ end
6685
+ assert_false(@res.full?)
6686
+ end
6687
+
6688
+ def test_flush
6689
+ @res << 'foo'
6690
+ @res << 'bar'
6691
+ r1 = @res.flush
6692
+ assert_equal('foo' + 'bar', r1.join(''), 'responses may be joined.')
6693
+ assert_true(@res.empty?)
6694
+ assert_false(@res.full?)
6695
+
6696
+ @res << 'baz'
6697
+ r2 = @res.flush
6698
+ assert_equal('baz', r2.join(''), 'responses may be joined.')
6699
+ assert_true(@res.empty?)
6700
+ assert_false(@res.full?)
6701
+
6702
+ assert_not_equal(r1, r2)
6703
+ end
6704
+
6705
+ def test_flush_joined
6706
+ for c in ('a'..).first(10)
6707
+ @res << c
6708
+ end
6709
+ assert_equal([ ('a'..).first(10).join('') ], @res.flush)
6710
+ assert_true(@res.empty?)
6711
+ assert_false(@res.full?)
6712
+ end
6713
+ end
6056
6714
  end
6057
6715
 
6058
6716
  # Local Variables: