rims 0.2.9 → 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.
@@ -33,21 +33,7 @@ module RIMS
33
33
  end
34
34
 
35
35
  class RequestReader
36
- def initialize(input, output, logger)
37
- @input = input
38
- @output = output
39
- @logger = logger
40
- end
41
-
42
- def read_line
43
- line = @input.gets or return
44
- @logger.debug("read line: #{Protocol.io_data_log(line)}") if @logger.debug?
45
- line.chomp!("\n")
46
- line.chomp!("\r")
47
- scan_line(line)
48
- end
49
-
50
- def scan_line(line)
36
+ def self.scan(line)
51
37
  atom_list = line.scan(/BODY(?:\.\S+)?\[.*?\](?:<\d+\.\d+>)?|[\[\]()]|".*?"|[^\[\]()\s]+/i).map{|s|
52
38
  case (s)
53
39
  when '(', ')', '[', ']', /\A NIL \z/ix
@@ -77,22 +63,14 @@ module RIMS
77
63
  end
78
64
  }
79
65
  if ((atom_list[-1].is_a? String) && (atom_list[-1] =~ /\A {\d+} \z/x)) then
80
- next_size = $&[1..-2].to_i
81
- @logger.debug("found literal: #{next_size} octets.") if @logger.debug?
82
- @output.write("+ continue\r\n")
83
- @output.flush
84
- @logger.debug('continue literal.') if @logger.debug?
85
- literal_string = @input.read(next_size) or raise 'unexpected client close.'
86
- @logger.debug("read literal: #{Protocol.io_data_log(literal_string)}") if @logger.debug?
87
- atom_list[-1] = literal_string
88
- next_atom_list = read_line or raise 'unexpected client close.'
89
- atom_list += next_atom_list
66
+ literal_size = $&[1..-2].to_i
67
+ atom_list[-1] = [ :literal, literal_size ]
90
68
  end
91
69
 
92
70
  atom_list
93
71
  end
94
72
 
95
- def parse(atom_list, last_atom=nil)
73
+ def self.parse(atom_list, last_atom=nil)
96
74
  syntax_list = []
97
75
  while (atom = atom_list.shift)
98
76
  case (atom)
@@ -105,7 +83,7 @@ module RIMS
105
83
  else
106
84
  if ((atom.is_a? Array) && (atom[0] == :body)) then
107
85
  body = atom[1]
108
- body.section_list = parse(scan_line(body.section))
86
+ body.section_list = parse(scan(body.section))
109
87
  end
110
88
  syntax_list.push(atom)
111
89
  end
@@ -118,18 +96,90 @@ module RIMS
118
96
  syntax_list
119
97
  end
120
98
 
99
+ def initialize(input, output, logger, line_length_limit: 1024*8, literal_size_limit: (1024**2)*10, command_size_limit: (1024**2)*10)
100
+ @input = input
101
+ @output = output
102
+ @logger = logger
103
+ @line_length_limit = line_length_limit
104
+ @literal_size_limit = literal_size_limit
105
+ @command_size_limit = command_size_limit
106
+ @command_tag = nil
107
+ @read_size = 0
108
+ end
109
+
110
+ attr_reader :command_tag
111
+
112
+ def gets
113
+ if (line = @input.gets($/, @line_length_limit)) then # arguments compatible with OpenSSL::Buffering#gets
114
+ if (line.bytesize < @line_length_limit) then
115
+ line
116
+ elsif (line.bytesize == @line_length_limit && (line.end_with? $/)) then
117
+ line
118
+ else
119
+ raise LineTooLongError.new('line too long.', line_fragment: line)
120
+ end
121
+ end
122
+ end
123
+
124
+ def read_literal(size)
125
+ @logger.debug("found literal: #{size} octets.") if @logger.debug?
126
+ if (size > @literal_size_limit) then
127
+ raise LiteralSizeTooLargeError.new('literal size too large', @command_tag)
128
+ end
129
+ if (@read_size + size > @command_size_limit) then
130
+ raise CommandSizeTooLargeError.new('command size too large', @command_tag)
131
+ end
132
+ @output.write("+ continue\r\n")
133
+ @output.flush
134
+ @logger.debug('continue literal.') if @logger.debug?
135
+ literal_string = @input.read(size) or raise 'unexpected client close.'
136
+ @read_size += size
137
+ @logger.debug("read literal: #{Protocol.io_data_log(literal_string)}") if @logger.debug?
138
+
139
+ literal_string
140
+ end
141
+ private :read_literal
142
+
143
+ def read_line
144
+ line = gets or return
145
+ @logger.debug("read line: #{Protocol.io_data_log(line)}") if @logger.debug?
146
+ line.chomp!("\n")
147
+ line.chomp!("\r")
148
+ @read_size += line.bytesize
149
+ if (@read_size > @command_size_limit) then
150
+ raise CommandSizeTooLargeError.new('command size too large', @command_tag)
151
+ end
152
+ atom_list = self.class.scan(line)
153
+
154
+ if (@command_tag.nil? && ! atom_list.empty?) then
155
+ unless ((atom_list[0].is_a? String) && ! (atom_list[0].start_with? '*', '+')) then
156
+ raise SyntaxError, "invalid command tag: #{atom_list[0]}"
157
+ end
158
+ @command_tag = atom_list[0]
159
+ end
160
+
161
+ if ((atom_list[-1].is_a? Array) && (atom_list[-1][0] == :literal)) then
162
+ atom_list[-1] = read_literal(atom_list[-1][1])
163
+ next_atom_list = read_line or raise 'unexpected client close.'
164
+ atom_list += next_atom_list
165
+ end
166
+
167
+ atom_list
168
+ end
169
+ private :read_line
170
+
121
171
  def read_command
172
+ @command_tag = nil
173
+ @read_size = 0
122
174
  while (atom_list = read_line)
123
175
  if (atom_list.empty?) then
176
+ @read_size = 0
124
177
  next
125
178
  end
126
179
  if (atom_list.length < 2) then
127
- raise 'need for tag and command.'
128
- end
129
- if (atom_list[0] =~ /\A [*+]/x) then
130
- raise "invalid command tag: #{atom_list[0]}"
180
+ raise SyntaxError, 'need for tag and command.'
131
181
  end
132
- return parse(atom_list)
182
+ return self.class.parse(atom_list)
133
183
  end
134
184
 
135
185
  nil
@@ -158,6 +158,10 @@ module RIMS
158
158
  # send_buffer_limit_size: 16384
159
159
  # read_polling_interval_seconds: 1
160
160
  # command_wait_timeout_seconds: 1800
161
+ # protocol:
162
+ # line_length_limit: 8192
163
+ # literal_size_limit: 10485760
164
+ # command_size_limit: 10485760
161
165
  # charset:
162
166
  # use_default_aliases: true
163
167
  # aliases:
@@ -176,6 +180,7 @@ module RIMS
176
180
  # load_limit: 134217728
177
181
  # engine:
178
182
  # bulk_response_count: 100
183
+ # bulk_response_size: 33554432
179
184
  # read_lock_timeout_seconds: 30
180
185
  # write_lock_timeout_seconds: 30
181
186
  # cleanup_write_lock_timeout_seconds: 1
@@ -566,6 +571,18 @@ module RIMS
566
571
  @config.dig('connection', 'command_wait_timeout_seconds') || 60 * 30)
567
572
  end
568
573
 
574
+ def protocol_line_length_limit
575
+ @config.dig('protocol', 'line_length_limit') || 1024 * 8
576
+ end
577
+
578
+ def protocol_literal_size_limit
579
+ @config.dig('protocol', 'literal_size_limit') || 1024**2 * 10
580
+ end
581
+
582
+ def protocol_command_size_limit
583
+ @config.dig('protocol', 'command_size_limit') || 1024**2 * 10
584
+ end
585
+
569
586
  def charset_aliases
570
587
  charset_aliases = RFC822::CharsetAliases.new
571
588
 
@@ -646,6 +663,10 @@ module RIMS
646
663
  @config.dig('drb_services', 'engine', 'bulk_response_count') || 100
647
664
  end
648
665
 
666
+ def bulk_response_size
667
+ @config.dig('drb_services', 'engine', 'bulk_response_size') || 1024**2 * 10
668
+ end
669
+
649
670
  def read_lock_timeout_seconds
650
671
  @config.dig('drb_services', 'engine', 'read_lock_timeout_seconds') ||
651
672
  @config.dig('read_lock_timeout_seconds') || # for backward compatibility
@@ -876,6 +897,7 @@ module RIMS
876
897
  mail_store = MailStore.build(unique_user_id, kvs_meta_open, kvs_text_open)
877
898
  Protocol::Decoder::Engine.new(unique_user_id, mail_store, logger,
878
899
  bulk_response_count: @config.bulk_response_count,
900
+ bulk_response_size: @config.bulk_response_size,
879
901
  read_lock_timeout_seconds: @config.read_lock_timeout_seconds,
880
902
  write_lock_timeout_seconds: @config.write_lock_timeout_seconds,
881
903
  cleanup_write_lock_timeout_seconds: @config.cleanup_write_lock_timeout_seconds,
@@ -986,6 +1008,9 @@ module RIMS
986
1008
  logger.info("connection parameter: send_buffer_limit_size=#{@config.send_buffer_limit_size}")
987
1009
  logger.info("connection parameter: read_polling_interval_seconds=#{conn_limits.read_polling_interval_seconds}")
988
1010
  logger.info("connection parameter: command_wait_timeout_seconds=#{conn_limits.command_wait_timeout_seconds}")
1011
+ logger.info("protocol parameter: line_length_limit=#{@config.protocol_line_length_limit}")
1012
+ logger.info("protocol parameter: literal_size_limit=#{@config.protocol_literal_size_limit}")
1013
+ logger.info("protocol parameter: command_size_limit=#{@config.protocol_command_size_limit}")
989
1014
  @config.charset_aliases.each_with_index do |(name, enc), i|
990
1015
  logger.info("charset aliases parameter: alias[#{i}]: #{name} -> #{enc.name}")
991
1016
  end
@@ -1000,6 +1025,7 @@ module RIMS
1000
1025
  logger.info("drb_services client config parameter: #{name}=#{value}")
1001
1026
  end
1002
1027
  logger.info("drb_services engine parameter: bulk_response_count=#{@config.bulk_response_count}")
1028
+ logger.info("drb_services engine parameter: bulk_response_size=#{@config.bulk_response_size}")
1003
1029
  logger.info("drb_services engine parameter: read_lock_timeout_seconds=#{@config.read_lock_timeout_seconds}")
1004
1030
  logger.info("drb_services engine parameter: write_lock_timeout_seconds=#{@config.write_lock_timeout_seconds}")
1005
1031
  logger.info("drb_services engine parameter: cleanup_write_lock_timeout_seconds=#{@config.cleanup_write_lock_timeout_seconds}")
@@ -1050,7 +1076,11 @@ module RIMS
1050
1076
  stream = Riser::WriteBufferStream.new(socket, @config.send_buffer_limit_size)
1051
1077
  end
1052
1078
  stream = Riser::LoggingStream.new(stream, protocol_logger)
1053
- decoder = Protocol::Decoder.new_decoder(drb_services, auth, logger, mail_delivery_user: @config.mail_delivery_user)
1079
+ decoder = Protocol::Decoder.new_decoder(drb_services, auth, logger,
1080
+ mail_delivery_user: @config.mail_delivery_user,
1081
+ line_length_limit: @config.protocol_line_length_limit,
1082
+ literal_size_limit: @config.protocol_literal_size_limit,
1083
+ command_size_limit: @config.protocol_command_size_limit)
1054
1084
  Protocol::Decoder.repl(decoder, conn_limits, stream, stream, logger)
1055
1085
  ensure
1056
1086
  if (stream) then
@@ -38,8 +38,7 @@ module RIMS
38
38
  module_function :message_data_list
39
39
 
40
40
  def make_body(description)
41
- reader = RIMS::Protocol::RequestReader.new(StringIO.new('', 'r'), StringIO.new('', 'w'), Logger.new(STDOUT))
42
- reader.parse(reader.scan_line(description))[0]
41
+ RIMS::Protocol::RequestReader.parse(RIMS::Protocol::RequestReader.scan(description))[0]
43
42
  end
44
43
  private :make_body
45
44
 
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module RIMS
4
- VERSION = '0.2.9'
4
+ VERSION = '0.3.0'
5
5
  MAILBOX_DATA_STRUCTURE_VERSION = 'mailbox.2'
6
6
  end
7
7
 
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.required_ruby_version = '>= 2.0.0'
28
28
 
29
29
  spec.add_runtime_dependency 'rims-rfc822', '>= 0.2.2'
30
- spec.add_runtime_dependency 'riser', '>= 0.1.12'
30
+ spec.add_runtime_dependency 'riser', '>= 0.2.0'
31
31
  spec.add_runtime_dependency 'logger-joint'
32
32
  spec.add_development_dependency 'bundler'
33
33
  spec.add_development_dependency 'rake'
@@ -141,6 +141,11 @@ module RIMS::Test
141
141
  '--read-polling-interval' => [ %W[ -f #{BASE_DIR}/config.yml --read-polling-interval=5 ] ],
142
142
  '--command-wait-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --command-wait-timeout=3600 ] ],
143
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
+
144
149
  # charset aliases:
145
150
  '--use-default-charset-aliases' => [ %W[ -f #{BASE_DIR}/config.yml --use-default-charset-aliases ] ],
146
151
  '--no-use-default-charset-aliases' => [ %W[ -f #{BASE_DIR}/config.yml --no-use-default-charset-aliases ] ],
@@ -159,6 +164,7 @@ module RIMS::Test
159
164
 
160
165
  # drb_services engine:
161
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 ] ],
162
168
  '--read-lock-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --read-lock-timeout=10 ] ],
163
169
  '--write-lock-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --write-lock-timeout=10 ] ],
164
170
  '--clenup-write-lock-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --write-lock-timeout=5 ] ],
@@ -4,7 +4,7 @@ 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
8
  def test_optional_data
9
9
  error = RIMS::Error.new('test', foo: 1, bar: '2')
10
10
  assert_equal('test', error.message)
@@ -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'
@@ -257,8 +257,15 @@ module RIMS::Test
257
257
  end
258
258
  private :open_mail_store
259
259
 
260
+ LINE_LENGTH_LIMIT = 128
261
+ LITERAL_SIZE_LIMIT = 1024**2
262
+ COMMAND_SIZE_LIMIT = LITERAL_SIZE_LIMIT + LINE_LENGTH_LIMIT
263
+
260
264
  def make_decoder
261
- RIMS::Protocol::Decoder.new_decoder(@drb_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)
262
269
  end
263
270
  private :make_decoder
264
271
 
@@ -380,6 +387,17 @@ module RIMS::Test
380
387
  end
381
388
  private :assert_imap_command
382
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
+
383
401
  def client_plain_response_base64(authentication_id, plain_password)
384
402
  response_txt = [ authentication_id, authentication_id, plain_password ].join("\0")
385
403
  RIMS::Protocol.encode_base64(response_txt)
@@ -562,6 +580,8 @@ module RIMS::Test
562
580
  assert.match(/^\* BYE /)
563
581
  assert.equal("#{tag} OK LOGOUT completed")
564
582
  }
583
+
584
+ assert_imap_closed
565
585
  }
566
586
  end
567
587
 
@@ -607,6 +627,8 @@ module RIMS::Test
607
627
  }
608
628
 
609
629
  assert_equal(false, @decoder.auth?) if command_test?
630
+
631
+ assert_imap_closed
610
632
  }
611
633
  end
612
634
 
@@ -658,6 +680,8 @@ module RIMS::Test
658
680
  }
659
681
 
660
682
  assert_equal(false, @decoder.auth?) if command_test?
683
+
684
+ assert_imap_closed
661
685
  }
662
686
  end
663
687
 
@@ -713,6 +737,8 @@ module RIMS::Test
713
737
  }
714
738
 
715
739
  assert_equal(false, @decoder.auth?) if command_test?
740
+
741
+ assert_imap_closed
716
742
  }
717
743
  end
718
744
 
@@ -755,6 +781,8 @@ module RIMS::Test
755
781
  }
756
782
 
757
783
  assert_equal(false, @decoder.auth?) if command_test?
784
+
785
+ assert_imap_closed
758
786
  }
759
787
  end
760
788
 
@@ -836,6 +864,8 @@ module RIMS::Test
836
864
  assert_equal(false, @decoder.selected?)
837
865
  end
838
866
 
867
+ assert_imap_closed
868
+
839
869
  open_mail_store{
840
870
  assert_msg_uid( 2, 3)
841
871
  assert_flag_enabled_msgs('answered', )
@@ -886,6 +916,8 @@ module RIMS::Test
886
916
  assert.match(/^\* BYE /)
887
917
  assert.equal("#{tag} OK LOGOUT completed")
888
918
  }
919
+
920
+ assert_imap_closed
889
921
  }
890
922
  end
891
923
 
@@ -967,6 +999,8 @@ module RIMS::Test
967
999
  assert_equal(false, @decoder.selected?)
968
1000
  end
969
1001
 
1002
+ assert_imap_closed
1003
+
970
1004
  open_mail_store{
971
1005
  assert_msg_uid( 1, 2, 3)
972
1006
  assert_flag_enabled_msgs('answered', )
@@ -1017,6 +1051,8 @@ module RIMS::Test
1017
1051
  assert.match(/^\* BYE /)
1018
1052
  assert.equal("#{tag} OK LOGOUT completed")
1019
1053
  }
1054
+
1055
+ assert_imap_closed
1020
1056
  }
1021
1057
  end
1022
1058
 
@@ -1066,6 +1102,8 @@ module RIMS::Test
1066
1102
  assert.match(/^\* BYE /)
1067
1103
  assert.equal("#{tag} OK LOGOUT completed")
1068
1104
  }
1105
+
1106
+ assert_imap_closed
1069
1107
  }
1070
1108
  end
1071
1109
 
@@ -1102,6 +1140,8 @@ module RIMS::Test
1102
1140
  assert.match(/^\* BYE /)
1103
1141
  assert.equal("#{tag} OK LOGOUT completed")
1104
1142
  }
1143
+
1144
+ assert_imap_closed
1105
1145
  }
1106
1146
  end
1107
1147
 
@@ -1174,6 +1214,8 @@ module RIMS::Test
1174
1214
  assert.match(/^\* BYE /)
1175
1215
  assert.equal("#{tag} OK LOGOUT completed")
1176
1216
  }
1217
+
1218
+ assert_imap_closed
1177
1219
  }
1178
1220
  end
1179
1221
 
@@ -1214,6 +1256,8 @@ module RIMS::Test
1214
1256
  assert.match(/^\* BYE /)
1215
1257
  assert.equal("#{tag} OK LOGOUT completed")
1216
1258
  }
1259
+
1260
+ assert_imap_closed
1217
1261
  }
1218
1262
  end
1219
1263
 
@@ -1293,6 +1337,8 @@ module RIMS::Test
1293
1337
  assert.match(/^\* BYE /)
1294
1338
  assert.equal("#{tag} OK LOGOUT completed")
1295
1339
  }
1340
+
1341
+ assert_imap_closed
1296
1342
  }
1297
1343
  end
1298
1344
 
@@ -1340,6 +1386,8 @@ module RIMS::Test
1340
1386
  assert.match(/^\* BYE /)
1341
1387
  assert.equal("#{tag} OK LOGOUT completed")
1342
1388
  }
1389
+
1390
+ assert_imap_closed
1343
1391
  }
1344
1392
  end
1345
1393
 
@@ -1409,6 +1457,8 @@ module RIMS::Test
1409
1457
  assert.match(/^\* BYE /)
1410
1458
  assert.equal("#{tag} OK LOGOUT completed")
1411
1459
  }
1460
+
1461
+ assert_imap_closed
1412
1462
  }
1413
1463
  end
1414
1464
 
@@ -1519,6 +1569,8 @@ module RIMS::Test
1519
1569
  assert.match(/^\* BYE /)
1520
1570
  assert.equal("#{tag} OK LOGOUT completed")
1521
1571
  }
1572
+
1573
+ assert_imap_closed
1522
1574
  }
1523
1575
  end
1524
1576
 
@@ -1557,6 +1609,8 @@ module RIMS::Test
1557
1609
  assert.match(/^\* BYE /)
1558
1610
  assert.equal("#{tag} OK LOGOUT completed")
1559
1611
  }
1612
+
1613
+ assert_imap_closed
1560
1614
  }
1561
1615
  end
1562
1616
 
@@ -1663,6 +1717,8 @@ module RIMS::Test
1663
1717
  assert.match(/^\* BYE /)
1664
1718
  assert.equal("#{tag} OK LOGOUT completed")
1665
1719
  }
1720
+
1721
+ assert_imap_closed
1666
1722
  }
1667
1723
  end
1668
1724
 
@@ -1694,6 +1750,8 @@ module RIMS::Test
1694
1750
  assert.match(/^\* BYE /)
1695
1751
  assert.equal("#{tag} OK LOGOUT completed")
1696
1752
  }
1753
+
1754
+ assert_imap_closed
1697
1755
  }
1698
1756
  end
1699
1757
 
@@ -1761,6 +1819,8 @@ module RIMS::Test
1761
1819
  assert.match(/^\* BYE /)
1762
1820
  assert.equal("#{tag} OK LOGOUT completed")
1763
1821
  }
1822
+
1823
+ assert_imap_closed
1764
1824
  }
1765
1825
  end
1766
1826
 
@@ -1881,6 +1941,8 @@ module RIMS::Test
1881
1941
  assert.match(/^\* BYE /)
1882
1942
  assert.equal("#{tag} OK LOGOUT completed")
1883
1943
  }
1944
+
1945
+ assert_imap_closed
1884
1946
  }
1885
1947
  end
1886
1948
 
@@ -1920,6 +1982,8 @@ module RIMS::Test
1920
1982
  assert.match(/^\* BYE /)
1921
1983
  assert.equal("#{tag} OK LOGOUT completed")
1922
1984
  }
1985
+
1986
+ assert_imap_closed
1923
1987
  }
1924
1988
  end
1925
1989
 
@@ -1928,6 +1992,69 @@ module RIMS::Test
1928
1992
  test_append_utf7_mbox_name
1929
1993
  end
1930
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
+
1931
2058
  def test_check
1932
2059
  imap_decode_engine_evaluate{
1933
2060
  if (stream_test?) then
@@ -1981,6 +2108,8 @@ module RIMS::Test
1981
2108
  assert.match(/^\* BYE /)
1982
2109
  assert.equal("#{tag} OK LOGOUT completed")
1983
2110
  }
2111
+
2112
+ assert_imap_closed
1984
2113
  }
1985
2114
  end
1986
2115
 
@@ -2092,6 +2221,8 @@ module RIMS::Test
2092
2221
  assert_equal(false, @decoder.auth?)
2093
2222
  assert_equal(false, @decoder.selected?)
2094
2223
  end
2224
+
2225
+ assert_imap_closed
2095
2226
  }
2096
2227
  end
2097
2228
 
@@ -2196,6 +2327,8 @@ module RIMS::Test
2196
2327
  assert_equal(false, @decoder.auth?)
2197
2328
  assert_equal(false, @decoder.selected?)
2198
2329
  end
2330
+
2331
+ assert_imap_closed
2199
2332
  }
2200
2333
  end
2201
2334
 
@@ -2346,6 +2479,8 @@ module RIMS::Test
2346
2479
  assert.match(/^\* BYE /)
2347
2480
  assert.equal("#{tag} OK LOGOUT completed")
2348
2481
  }
2482
+
2483
+ assert_imap_closed
2349
2484
  }
2350
2485
  end
2351
2486
 
@@ -2422,6 +2557,8 @@ module RIMS::Test
2422
2557
  assert.match(/^\* BYE /)
2423
2558
  assert.equal("#{tag} OK LOGOUT completed")
2424
2559
  }
2560
+
2561
+ assert_imap_closed
2425
2562
  }
2426
2563
  end
2427
2564
 
@@ -2551,6 +2688,8 @@ module RIMS::Test
2551
2688
  assert.match(/^\* BYE /)
2552
2689
  assert.equal("#{tag} OK LOGOUT completed")
2553
2690
  }
2691
+
2692
+ assert_imap_closed
2554
2693
  }
2555
2694
  end
2556
2695
 
@@ -2637,13 +2776,13 @@ module RIMS::Test
2637
2776
  }
2638
2777
 
2639
2778
  utf8_msg = "\u306F\u306B\u307B"
2640
- 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|
2641
2780
  assert.match(/^\+ /)
2642
2781
  assert.equal("* SEARCH 4 5\r\n")
2643
2782
  assert.equal("#{tag} OK SEARCH completed\r\n")
2644
2783
  }
2645
2784
 
2646
- assert_imap_command("SEARCH CHARSET euc-jp BODY {#{utf8_msg.bytesize}}\r\n#{utf8_msg}".b) {|assert|
2785
+ assert_imap_command("SEARCH CHARSET euc-jp BODY #{literal(utf8_msg)}".b) {|assert|
2647
2786
  assert.match(/^\+ /)
2648
2787
  assert.match(/^#{tag} BAD /, peek_next_line: true).match(/syntax error/)
2649
2788
  }
@@ -2668,6 +2807,8 @@ module RIMS::Test
2668
2807
  assert.match(/^\* BYE /)
2669
2808
  assert.equal("#{tag} OK LOGOUT completed")
2670
2809
  }
2810
+
2811
+ assert_imap_closed
2671
2812
  }
2672
2813
  end
2673
2814
 
@@ -2761,13 +2902,13 @@ module RIMS::Test
2761
2902
  }
2762
2903
 
2763
2904
  utf8_msg = "\u306F\u306B\u307B"
2764
- 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|
2765
2906
  assert.match(/^\+ /)
2766
2907
  assert.equal("* SEARCH 4 5\r\n")
2767
2908
  assert.equal("#{tag} OK SEARCH completed\r\n")
2768
2909
  }
2769
2910
 
2770
- assert_imap_command("SEARCH CHARSET euc-jp TEXT {#{utf8_msg.bytesize}}\r\n#{utf8_msg}".b) {|assert|
2911
+ assert_imap_command("SEARCH CHARSET euc-jp TEXT #{literal(utf8_msg)}".b) {|assert|
2771
2912
  assert.match(/^\+ /)
2772
2913
  assert.match(/^#{tag} BAD /, peek_next_line: true).match(/syntax error/)
2773
2914
  }
@@ -2792,6 +2933,8 @@ module RIMS::Test
2792
2933
  assert.match(/^\* BYE /)
2793
2934
  assert.equal("#{tag} OK LOGOUT completed")
2794
2935
  }
2936
+
2937
+ assert_imap_closed
2795
2938
  }
2796
2939
  end
2797
2940
 
@@ -2853,7 +2996,7 @@ module RIMS::Test
2853
2996
  }
2854
2997
 
2855
2998
  utf8_msg = "\u306F\u306B\u307B"
2856
- assert_imap_command("SEARCH CHARSET utf-8 TEXT {#{utf8_msg.bytesize}}\r\n#{utf8_msg}".b) {|assert|
2999
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(utf8_msg)}".b) {|assert|
2857
3000
  assert.match(/^\+ /)
2858
3001
  assert.equal("* SEARCH\r\n")
2859
3002
  assert.equal("#{tag} OK SEARCH completed\r\n")
@@ -2863,6 +3006,8 @@ module RIMS::Test
2863
3006
  assert.match(/^\* BYE /)
2864
3007
  assert.equal("#{tag} OK LOGOUT completed")
2865
3008
  }
3009
+
3010
+ assert_imap_closed
2866
3011
  }
2867
3012
  end
2868
3013
 
@@ -2927,6 +3072,8 @@ module RIMS::Test
2927
3072
  assert.match(/^\* BYE /)
2928
3073
  assert.equal("#{tag} OK LOGOUT completed")
2929
3074
  }
3075
+
3076
+ assert_imap_closed
2930
3077
  }
2931
3078
  end
2932
3079
 
@@ -3080,6 +3227,8 @@ module RIMS::Test
3080
3227
  assert.match(/^\* BYE /)
3081
3228
  assert.equal("#{tag} OK LOGOUT completed")
3082
3229
  }
3230
+
3231
+ assert_imap_closed
3083
3232
  }
3084
3233
  end
3085
3234
 
@@ -3232,6 +3381,8 @@ module RIMS::Test
3232
3381
  assert.match(/^\* BYE /)
3233
3382
  assert.equal("#{tag} OK LOGOUT completed")
3234
3383
  }
3384
+
3385
+ assert_imap_closed
3235
3386
  }
3236
3387
  end
3237
3388
 
@@ -3584,6 +3735,8 @@ module RIMS::Test
3584
3735
  assert.match(/^\* BYE /)
3585
3736
  assert.equal("#{tag} OK LOGOUT completed")
3586
3737
  }
3738
+
3739
+ assert_imap_closed
3587
3740
  }
3588
3741
  end
3589
3742
 
@@ -3886,6 +4039,8 @@ module RIMS::Test
3886
4039
  assert.match(/^\* BYE /)
3887
4040
  assert.equal("#{tag} OK LOGOUT completed")
3888
4041
  }
4042
+
4043
+ assert_imap_closed
3889
4044
  }
3890
4045
  end
3891
4046
 
@@ -4238,6 +4393,8 @@ module RIMS::Test
4238
4393
  assert.match(/^\* BYE /)
4239
4394
  assert.equal("#{tag} OK LOGOUT completed")
4240
4395
  }
4396
+
4397
+ assert_imap_closed
4241
4398
  }
4242
4399
  end
4243
4400
 
@@ -4540,6 +4697,8 @@ module RIMS::Test
4540
4697
  assert.match(/^\* BYE /)
4541
4698
  assert.equal("#{tag} OK LOGOUT completed")
4542
4699
  }
4700
+
4701
+ assert_imap_closed
4543
4702
  }
4544
4703
  end
4545
4704
 
@@ -4709,6 +4868,8 @@ module RIMS::Test
4709
4868
  assert.match(/^\* BYE /)
4710
4869
  assert.equal("#{tag} OK LOGOUT completed")
4711
4870
  }
4871
+
4872
+ assert_imap_closed
4712
4873
  }
4713
4874
  end
4714
4875
 
@@ -4913,6 +5074,8 @@ module RIMS::Test
4913
5074
  assert.match(/^\* BYE /)
4914
5075
  assert.equal("#{tag} OK LOGOUT completed")
4915
5076
  }
5077
+
5078
+ assert_imap_closed
4916
5079
  }
4917
5080
  end
4918
5081
 
@@ -5117,6 +5280,8 @@ module RIMS::Test
5117
5280
  assert.match(/^\* BYE /)
5118
5281
  assert.equal("#{tag} OK LOGOUT completed")
5119
5282
  }
5283
+
5284
+ assert_imap_closed
5120
5285
  }
5121
5286
  end
5122
5287
 
@@ -5170,6 +5335,8 @@ module RIMS::Test
5170
5335
  assert.match(/^\* BYE /)
5171
5336
  assert.equal("#{tag} OK LOGOUT completed")
5172
5337
  }
5338
+
5339
+ assert_imap_closed
5173
5340
  }
5174
5341
  end
5175
5342
 
@@ -5262,6 +5429,8 @@ module RIMS::Test
5262
5429
  assert_equal(false, @decoder.auth?)
5263
5430
  assert_equal(false, @decoder.selected?)
5264
5431
  end
5432
+
5433
+ assert_imap_closed
5265
5434
  }
5266
5435
  end
5267
5436
 
@@ -5370,6 +5539,8 @@ module RIMS::Test
5370
5539
  assert_equal(false, @decoder.auth?)
5371
5540
  assert_equal(false, @decoder.selected?)
5372
5541
  end
5542
+
5543
+ assert_imap_closed
5373
5544
  }
5374
5545
  end
5375
5546
 
@@ -5379,11 +5550,11 @@ module RIMS::Test
5379
5550
  end
5380
5551
 
5381
5552
  def test_error_handling_stream
5382
- use_imap_stream_decode_engine
5553
+ use_imap_stream_decode_engine # always run in stream
5383
5554
  imap_decode_engine_evaluate{
5384
5555
  assert_imap_command('') {|assert|
5385
5556
  assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
5386
- assert.equal('* BAD client command syntax error')
5557
+ assert.equal("#{tag} BAD client command syntax error")
5387
5558
  }
5388
5559
 
5389
5560
  assert_imap_command('no_command') {|assert|
@@ -5401,6 +5572,30 @@ module RIMS::Test
5401
5572
  assert_imap_command('noop detarame') {|assert|
5402
5573
  assert.equal("#{tag} BAD invalid command parameter")
5403
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
+ }
5404
5599
  }
5405
5600
  end
5406
5601
 
@@ -5432,6 +5627,8 @@ module RIMS::Test
5432
5627
  }
5433
5628
 
5434
5629
  assert_equal(false, @decoder.auth?) if command_test?
5630
+
5631
+ assert_imap_closed
5435
5632
  }
5436
5633
  end
5437
5634
 
@@ -5588,6 +5785,8 @@ module RIMS::Test
5588
5785
  }
5589
5786
 
5590
5787
  assert_equal(false, @decoder.auth?) if command_test?
5788
+
5789
+ assert_imap_closed
5591
5790
  }
5592
5791
  end
5593
5792
 
@@ -5646,6 +5845,8 @@ module RIMS::Test
5646
5845
  }
5647
5846
 
5648
5847
  assert_equal(false, @decoder.auth?) if command_test?
5848
+
5849
+ assert_imap_closed
5649
5850
  }
5650
5851
  end
5651
5852
 
@@ -6249,6 +6450,8 @@ module RIMS::Test
6249
6450
  assert_equal(false, @decoder.auth?)
6250
6451
  assert_equal(false, @decoder.selected?)
6251
6452
  end
6453
+
6454
+ assert_imap_closed
6252
6455
  }
6253
6456
  end
6254
6457
 
@@ -6330,6 +6533,8 @@ module RIMS::Test
6330
6533
  assert_equal(false, @decoder.auth?)
6331
6534
  assert_equal(false, @decoder.selected?)
6332
6535
  end
6536
+
6537
+ assert_imap_closed
6333
6538
  }
6334
6539
  end
6335
6540
 
@@ -6337,6 +6542,66 @@ module RIMS::Test
6337
6542
  use_imap_stream_decode_engine
6338
6543
  test_charset_convert_options
6339
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
6340
6605
  end
6341
6606
 
6342
6607
  class ProtocolMailDeliveryDecoderTest < Test::Unit::TestCase