rims 0.2.6 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +97 -0
- data/ChangeLog +49 -0
- data/Rakefile +8 -4
- data/lib/rims.rb +31 -29
- data/lib/rims/channel.rb +35 -10
- data/lib/rims/cmd.rb +109 -3
- data/lib/rims/error.rb +15 -0
- data/lib/rims/protocol.rb +28 -7
- data/lib/rims/protocol/decoder.rb +727 -457
- data/lib/rims/protocol/parser.rb +190 -82
- data/lib/rims/service.rb +172 -39
- data/lib/rims/test.rb +31 -20
- data/lib/rims/version.rb +1 -1
- data/load_test/Rakefile +3 -1
- data/rims.gemspec +14 -13
- data/test/cmd/test_command.rb +21 -1
- data/test/test_channel.rb +2 -1
- data/test/test_error.rb +33 -1
- data/test/test_lock.rb +4 -4
- data/test/test_passwd.rb +1 -1
- data/test/test_protocol_decoder.rb +555 -41
- data/test/test_protocol_fetch.rb +14 -10
- data/test/test_protocol_request.rb +305 -280
- data/test/test_protocol_search.rb +771 -893
- data/test/test_service.rb +165 -0
- metadata +22 -7
data/test/test_protocol_fetch.rb
CHANGED
@@ -516,25 +516,27 @@ module RIMS::Test
|
|
516
516
|
assert_fetch(2,
|
517
517
|
[
|
518
518
|
'BODY ' +
|
519
|
-
encode_bodystructure([ '
|
520
|
-
'
|
519
|
+
encode_bodystructure([ 'TEXT',
|
520
|
+
'PLAIN',
|
521
521
|
nil,
|
522
522
|
nil,
|
523
523
|
nil,
|
524
524
|
nil,
|
525
|
-
@empty_mail.raw_source.bytesize
|
525
|
+
@empty_mail.raw_source.bytesize,
|
526
|
+
@empty_mail.raw_source.each_line.count
|
526
527
|
])
|
527
528
|
])
|
528
529
|
assert_fetch(3,
|
529
530
|
[
|
530
531
|
'BODY ' +
|
531
|
-
encode_bodystructure([ '
|
532
|
-
'
|
532
|
+
encode_bodystructure([ 'TEXT',
|
533
|
+
'PLAIN',
|
533
534
|
nil,
|
534
535
|
nil,
|
535
536
|
nil,
|
536
537
|
nil,
|
537
|
-
@no_body_mail.raw_source.bytesize
|
538
|
+
@no_body_mail.raw_source.bytesize,
|
539
|
+
@no_body_mail.raw_source.each_line.count
|
538
540
|
])
|
539
541
|
])
|
540
542
|
}
|
@@ -654,13 +656,14 @@ module RIMS::Test
|
|
654
656
|
])
|
655
657
|
assert_fetch(2, [
|
656
658
|
'BODYSTRUCTURE ' +
|
657
|
-
encode_bodystructure([ '
|
658
|
-
'
|
659
|
+
encode_bodystructure([ 'TEXT',
|
660
|
+
'PLAIN',
|
659
661
|
nil,
|
660
662
|
nil,
|
661
663
|
nil,
|
662
664
|
nil,
|
663
665
|
@empty_mail.raw_source.bytesize,
|
666
|
+
@empty_mail.raw_source.each_line.count,
|
664
667
|
nil,
|
665
668
|
nil,
|
666
669
|
nil,
|
@@ -669,13 +672,14 @@ module RIMS::Test
|
|
669
672
|
])
|
670
673
|
assert_fetch(3, [
|
671
674
|
'BODYSTRUCTURE ' +
|
672
|
-
encode_bodystructure([ '
|
673
|
-
'
|
675
|
+
encode_bodystructure([ 'TEXT',
|
676
|
+
'PLAIN',
|
674
677
|
nil,
|
675
678
|
nil,
|
676
679
|
nil,
|
677
680
|
nil,
|
678
681
|
@no_body_mail.raw_source.bytesize,
|
682
|
+
@no_body_mail.raw_source.each_line.count,
|
679
683
|
nil,
|
680
684
|
nil,
|
681
685
|
nil,
|
@@ -2,124 +2,63 @@
|
|
2
2
|
|
3
3
|
require 'logger'
|
4
4
|
require 'rims'
|
5
|
+
require 'stringio'
|
5
6
|
require 'test/unit'
|
6
7
|
|
7
8
|
module RIMS::Test
|
8
9
|
class ProtocolRequestReaderTest < Test::Unit::TestCase
|
10
|
+
extend AssertUtility
|
11
|
+
include AssertUtility
|
12
|
+
|
13
|
+
LINE_LENGTH_LIMIT = 128
|
14
|
+
LITERAL_SIZE_LIMIT = 1024**2
|
15
|
+
COMMAND_SIZE_LIMIT = LITERAL_SIZE_LIMIT + LINE_LENGTH_LIMIT
|
16
|
+
|
9
17
|
def setup
|
10
18
|
@input = StringIO.new('', 'r')
|
11
19
|
@output = StringIO.new('', 'w')
|
12
20
|
@logger = Logger.new(STDOUT)
|
13
21
|
@logger.level = ($DEBUG) ? Logger::DEBUG : Logger::FATAL
|
14
|
-
@reader = RIMS::Protocol::RequestReader.new(@input, @output, @logger
|
22
|
+
@reader = RIMS::Protocol::RequestReader.new(@input, @output, @logger,
|
23
|
+
line_length_limit: LINE_LENGTH_LIMIT,
|
24
|
+
literal_size_limit: LITERAL_SIZE_LIMIT,
|
25
|
+
command_size_limit: COMMAND_SIZE_LIMIT)
|
15
26
|
end
|
16
27
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ], @reader.scan_line('* LIST (\Noselect) NIL ""'))
|
34
|
-
assert_equal([ 'A654', 'FETCH', '2:4',
|
35
|
-
'('.intern,
|
36
|
-
[ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
|
37
|
-
')'.intern
|
38
|
-
],
|
39
|
-
@reader.scan_line('A654 FETCH 2:4 (BODY[])'))
|
40
|
-
|
41
|
-
assert_equal('', @output.string)
|
28
|
+
data('EOF' => [
|
29
|
+
nil,
|
30
|
+
''
|
31
|
+
],
|
32
|
+
'short_line_length' => [
|
33
|
+
"foo\r\n",
|
34
|
+
"foo\r\n"
|
35
|
+
],
|
36
|
+
'upper_bound_line_length' => [
|
37
|
+
'x' * (LINE_LENGTH_LIMIT - 2) + "\r\n",
|
38
|
+
'x' * (LINE_LENGTH_LIMIT - 2) + "\r\n"
|
39
|
+
])
|
40
|
+
def test_gets(data)
|
41
|
+
expected_line, input_line = data
|
42
|
+
@input.string = input_line
|
43
|
+
assert_equal(expected_line, @reader.gets)
|
42
44
|
end
|
43
45
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
line = 'A003 APPEND saved-messages (\Seen) ' + "{#{literal.bytesize}}"
|
58
|
-
@input.string = literal + "\r\n"
|
59
|
-
|
60
|
-
assert_equal([ 'A003', 'APPEND', 'saved-messages', '('.intern, '\Seen', ')'.intern, literal ], @reader.scan_line(line))
|
61
|
-
assert_equal('', @input.read)
|
62
|
-
|
63
|
-
cmd_cont_req = @output.string.each_line
|
64
|
-
assert_match(/^\+ /, cmd_cont_req.next)
|
65
|
-
assert_raise(StopIteration) { cmd_cont_req.next }
|
46
|
+
data('too_long_line_length' => [
|
47
|
+
'x' * LINE_LENGTH_LIMIT,
|
48
|
+
'x' * LINE_LENGTH_LIMIT + "yyy\r\n"
|
49
|
+
],
|
50
|
+
'upper_bound_line_length' => [
|
51
|
+
'x' * LINE_LENGTH_LIMIT,
|
52
|
+
'x' * LINE_LENGTH_LIMIT
|
53
|
+
])
|
54
|
+
def test_gets_line_too_long_error
|
55
|
+
expected_line_fragment, input_line = data
|
56
|
+
@input.string = input_line
|
57
|
+
error = assert_raise(RIMS::LineTooLongError) { @reader.gets }
|
58
|
+
assert_equal(expected_line_fragment, error.optional_data[:line_fragment])
|
66
59
|
end
|
67
60
|
|
68
|
-
|
69
|
-
assert_nil(@reader.read_line)
|
70
|
-
|
71
|
-
@input.string = "\n"
|
72
|
-
assert_equal([], @reader.read_line)
|
73
|
-
|
74
|
-
@input.string = "abcd CAPABILITY\n"
|
75
|
-
assert_equal(%w[ abcd CAPABILITY ], @reader.read_line)
|
76
|
-
|
77
|
-
@input.string = "abcd OK CAPABILITY completed\n"
|
78
|
-
assert_equal(%w[ abcd OK CAPABILITY completed ], @reader.read_line)
|
79
|
-
|
80
|
-
@input.string = "* CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4\n"
|
81
|
-
assert_equal(%w[ * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4 ], @reader.read_line)
|
82
|
-
|
83
|
-
@input.string = "* 172 EXISTS\n"
|
84
|
-
assert_equal(%w[ * 172 EXISTS ], @reader.read_line)
|
85
|
-
|
86
|
-
@input.string = "* OK [UNSEEN 12] Message 12 is first unseen\n"
|
87
|
-
assert_equal([ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen', ], @reader.read_line)
|
88
|
-
|
89
|
-
@input.string = "* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\n"
|
90
|
-
assert_equal([ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ], @reader.read_line)
|
91
|
-
|
92
|
-
@input.string = "* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited\n"
|
93
|
-
assert_equal([ '*', 'OK',
|
94
|
-
'['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern,
|
95
|
-
'Limited'
|
96
|
-
], @reader.read_line)
|
97
|
-
|
98
|
-
@input.string = "A82 LIST \"\" *\n"
|
99
|
-
assert_equal([ 'A82', 'LIST', '', '*' ], @reader.read_line)
|
100
|
-
|
101
|
-
@input.string = "* LIST (\\Noselect) \"/\" foo\n"
|
102
|
-
assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo' ], @reader.read_line)
|
103
|
-
|
104
|
-
@input.string = "* LIST (\\Noselect) \"/\" \"foo [bar] (baz)\""
|
105
|
-
assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo [bar] (baz)' ], @reader.read_line)
|
106
|
-
|
107
|
-
@input.string = '* LIST (\Noselect) NIL ""'
|
108
|
-
assert_equal([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ], @reader.read_line)
|
109
|
-
|
110
|
-
@input.string = "A654 FETCH 2:4 (BODY[])\n"
|
111
|
-
assert_equal([ 'A654', 'FETCH', '2:4',
|
112
|
-
'('.intern,
|
113
|
-
[ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
|
114
|
-
')'.intern
|
115
|
-
],
|
116
|
-
@reader.read_line)
|
117
|
-
|
118
|
-
assert_equal('', @output.string)
|
119
|
-
end
|
120
|
-
|
121
|
-
def test_read_line_string_literal
|
122
|
-
literal = <<-'EOF'
|
61
|
+
LITERAL_1 = <<-'EOF'
|
123
62
|
Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
|
124
63
|
From: Fred Foobar <foobar@Blurdybloop.COM>
|
125
64
|
Subject: afternoon meeting
|
@@ -129,34 +68,133 @@ MIME-Version: 1.0
|
|
129
68
|
Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
|
130
69
|
|
131
70
|
Hello Joe, do you think we can meet at 3:30 tomorrow?
|
132
|
-
|
71
|
+
EOF
|
133
72
|
|
134
|
-
|
135
|
-
|
136
|
-
assert_equal('', @input.read)
|
73
|
+
LITERAL_2 = <<-'EOF'
|
74
|
+
Subject: parse test
|
137
75
|
|
138
|
-
|
139
|
-
|
140
|
-
|
76
|
+
body[]
|
77
|
+
EOF
|
78
|
+
|
79
|
+
LITERAL_3 = 'body[]'
|
80
|
+
|
81
|
+
data('empty' => [ nil, nil ],
|
82
|
+
'newline' => [ nil, "\n" ],
|
83
|
+
'whitespaces' => [ nil, " \t\n" ],
|
84
|
+
'tagged_command' => [ %w[ abcd CAPABILITY ], "abcd CAPABILITY\n" ],
|
85
|
+
'tagged_command_with_whitespaces' => [ %w[ abcd CAPABILITY ], "\n \n\t\nabcd CAPABILITY\n" ],
|
86
|
+
'tagged_response' => [ %w[ abcd OK CAPABILITY completed ], "abcd OK CAPABILITY completed\n" ],
|
87
|
+
'group' => [
|
88
|
+
[ 'A003', 'STORE', '2:4', '+FLAGS', [ :group, '\Deleted' ] ],
|
89
|
+
"A003 STORE 2:4 +FLAGS (\\Deleted)\n"
|
90
|
+
],
|
91
|
+
'nested_group' => [
|
92
|
+
[ 'abcd', 'SEARCH',
|
93
|
+
[ :group,
|
94
|
+
'OR',
|
95
|
+
[ :group, 'FROM', 'foo' ],
|
96
|
+
[ :group, 'FROM', 'bar' ]
|
97
|
+
]
|
98
|
+
],
|
99
|
+
"abcd SEARCH (OR (FROM foo) (FROM bar))\n"
|
100
|
+
],
|
101
|
+
'quoted_special_nil' => [
|
102
|
+
[ 'abcd', 'SEARCH', 'SUBJECT', 'NIL' ],
|
103
|
+
"abcd SEARCH SUBJECT \"NIL\"\n"
|
104
|
+
],
|
105
|
+
'quoted_special_parenthesis_begin' => [
|
106
|
+
[ 'abcd', 'SEARCH', 'SUBJECT', '(' ],
|
107
|
+
"abcd SEARCH SUBJECT \"(\"\n"
|
108
|
+
],
|
109
|
+
'quoted_special_parenthesis_end' => [
|
110
|
+
[ 'abcd', 'SEARCH', 'SUBJECT', ')' ],
|
111
|
+
"abcd SEARCH SUBJECT \")\"\n"
|
112
|
+
],
|
113
|
+
'quoted_special_square_bracket_begin' => [
|
114
|
+
[ 'abcd', 'SEARCH', 'SUBJECT', '[' ],
|
115
|
+
"abcd SEARCH SUBJECT \"[\"\n"
|
116
|
+
],
|
117
|
+
'quoted_special_square_bracket_end' => [
|
118
|
+
[ 'abcd', 'SEARCH', 'SUBJECT', ']' ],
|
119
|
+
"abcd SEARCH SUBJECT \"]\"\n"
|
120
|
+
],
|
121
|
+
'fetch_non_extensible_bodystructure' => [
|
122
|
+
[ 'A654', 'FETCH', '2:4', [ :group, 'BODY' ] ],
|
123
|
+
"A654 FETCH 2:4 (BODY)\n"
|
124
|
+
],
|
125
|
+
'fetch_body_section' => [
|
126
|
+
[ 'A654', 'FETCH', '2:4', [
|
127
|
+
:group, [
|
128
|
+
:body,
|
129
|
+
RIMS::Protocol.body(symbol: 'BODY',
|
130
|
+
section: '',
|
131
|
+
section_list: [])
|
132
|
+
]
|
133
|
+
]
|
134
|
+
],
|
135
|
+
"A654 FETCH 2:4 (BODY[])\n"
|
136
|
+
],
|
137
|
+
'fetch_multiple_items' => [
|
138
|
+
[ 'A654', 'FETCH', '2:4',
|
139
|
+
[ :group,
|
140
|
+
'FLAGS',
|
141
|
+
[ :body,
|
142
|
+
RIMS::Protocol.body(symbol: 'BODY',
|
143
|
+
option: 'PEEK',
|
144
|
+
section: 'HEADER.FIELDS (DATE FROM)',
|
145
|
+
section_list: [ 'HEADER.FIELDS', [ :group, 'DATE', 'FROM' ] ],
|
146
|
+
partial_origin: 0,
|
147
|
+
partial_size: 1500)
|
148
|
+
]
|
149
|
+
]
|
150
|
+
],
|
151
|
+
"A654 FETCH 2:4 (FLAGS BODY.PEEK[HEADER.FIELDS (DATE FROM)]<0.1500>)\n"
|
152
|
+
],
|
153
|
+
'append_inline' => [
|
154
|
+
[ 'A654', 'APPEND', 'saved-messages', 'body[]' ],
|
155
|
+
"A654 APPEND saved-messages \"body[]\"\n"
|
156
|
+
],
|
157
|
+
'append_literal_1' => [
|
158
|
+
[ 'A003', 'APPEND', 'saved-messages', [ :group, '\Seen' ], LITERAL_1 ],
|
159
|
+
"A003 APPEND saved-messages (\\Seen) #{literal(LITERAL_1)}\n",
|
160
|
+
true
|
161
|
+
],
|
162
|
+
'append_literal_2' => [
|
163
|
+
[ 'A004', 'APPEND', 'saved-messages', LITERAL_2 ],
|
164
|
+
"A004 APPEND saved-messages #{literal(LITERAL_2)}\n",
|
165
|
+
true
|
166
|
+
],
|
167
|
+
'append_literal_3' => [
|
168
|
+
[ 'A005', 'APPEND', 'saved-messages', LITERAL_3 ],
|
169
|
+
"A005 APPEND saved-messages #{literal(LITERAL_3)}\n",
|
170
|
+
true
|
171
|
+
],
|
172
|
+
'append_literal_size_limit' => [
|
173
|
+
[ 'A005', 'APPEND', 'saved-messages', 'x' * LITERAL_SIZE_LIMIT ],
|
174
|
+
"A005 APPEND saved-messages #{literal('x' * LITERAL_SIZE_LIMIT)}\n",
|
175
|
+
true
|
176
|
+
])
|
177
|
+
def test_read_command(data)
|
178
|
+
expected_atom_list, input_string, is_literal = data
|
179
|
+
@input.string = input_string if input_string
|
180
|
+
assert_equal(expected_atom_list, @reader.read_command)
|
181
|
+
assert_equal(expected_atom_list[0], @reader.command_tag) if expected_atom_list
|
182
|
+
|
183
|
+
if (is_literal) then
|
184
|
+
cmd_cont_req = @output.string.each_line
|
185
|
+
assert_match(/^\+ /, cmd_cont_req.next)
|
186
|
+
assert_raise(StopIteration) { cmd_cont_req.next }
|
187
|
+
else
|
188
|
+
assert_equal('', @output.string)
|
189
|
+
end
|
141
190
|
end
|
142
191
|
|
143
|
-
def
|
144
|
-
|
145
|
-
Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
|
146
|
-
From: Fred Foobar <foobar@Blurdybloop.COM>
|
147
|
-
Subject: afternoon meeting
|
148
|
-
To: mooch@owatagu.siam.edu
|
149
|
-
Message-Id: <B27397-0100000@Blurdybloop.COM>
|
150
|
-
MIME-Version: 1.0
|
151
|
-
Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
|
152
|
-
EOF
|
153
|
-
|
154
|
-
literal2 = <<-'EOF'
|
155
|
-
Hello Joe, do you think we can meet at 3:30 tomorrow?
|
156
|
-
EOF
|
192
|
+
def test_read_command_string_literal_multi
|
193
|
+
literal_pair = RIMS::RFC822::Parse.split_message(LITERAL_1)
|
157
194
|
|
158
|
-
@input.string = "
|
159
|
-
assert_equal([ '
|
195
|
+
@input.string = "A284 SEARCH CHARSET UTF-8 TEXT #{literal(literal_pair[0])} TEXT #{literal(literal_pair[1])}\n"
|
196
|
+
assert_equal([ 'A284', 'SEARCH', 'CHARSET', 'UTF-8', 'TEXT', literal_pair[0], 'TEXT', literal_pair[1] ], @reader.read_command)
|
197
|
+
assert_equal('A284', @reader.command_tag)
|
160
198
|
assert_equal('', @input.read)
|
161
199
|
|
162
200
|
cmd_cont_req = @output.string.each_line
|
@@ -165,169 +203,156 @@ Hello Joe, do you think we can meet at 3:30 tomorrow?
|
|
165
203
|
assert_raise(StopIteration) { cmd_cont_req.next }
|
166
204
|
end
|
167
205
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
assert_equal([ '*', 'OK', [ :block, 'UNSEEN', '12' ], 'Message', '12', 'is', 'first', 'unseen' ],
|
173
|
-
@reader.parse([ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ]))
|
174
|
-
assert_equal([ '*', 'FLAGS', [ :group, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft' ] ],
|
175
|
-
@reader.parse([ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ]))
|
176
|
-
assert_equal([ '*', 'OK', [ :block, 'PERMANENTFLAGS', [ :group, '\Deleted', '\Seen', '\*' ] ], 'Limited' ],
|
177
|
-
@reader.parse([ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ]))
|
178
|
-
assert_equal([ '*', 'LIST', [ :group, '\Noselect' ], :NIL, '' ],
|
179
|
-
@reader.parse([ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ]))
|
180
|
-
assert_equal([ 'A654', 'FETCH', '2:4',
|
181
|
-
[ :group,
|
182
|
-
[ :body,
|
183
|
-
RIMS::Protocol.body(symbol: 'BODY',
|
184
|
-
section: '',
|
185
|
-
section_list: [])
|
186
|
-
]
|
187
|
-
]
|
188
|
-
],
|
189
|
-
@reader.parse([ 'A654', 'FETCH', '2:4',
|
190
|
-
'('.intern,
|
191
|
-
[ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
|
192
|
-
')'.intern
|
193
|
-
]))
|
194
|
-
assert_equal([ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ],
|
195
|
-
@reader.parse([ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ]))
|
196
|
-
|
197
|
-
error = assert_raise(RIMS::SyntaxError) { @reader.parse([ '*', 'OK', '['.intern, 'UNSEEN', '12' ]) }
|
198
|
-
assert_match(/not found a terminator/, error.message)
|
199
|
-
|
200
|
-
error = assert_raise(RIMS::SyntaxError) { @reader.parse([ '*', 'LIST', '('.intern, '\Noselect' ]) }
|
201
|
-
assert_match(/not found a terminator/, error.message)
|
202
|
-
|
203
|
-
assert_equal('', @output.string)
|
206
|
+
def test_read_command_no_tag_error
|
207
|
+
@input.string = "noop\r\n"
|
208
|
+
error = assert_raise(RIMS::SyntaxError) { @reader.read_command }
|
209
|
+
assert_match(/need for tag/, error.message)
|
204
210
|
end
|
205
211
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
assert_equal(%w[ abcd CAPABILITY ], @reader.read_command)
|
217
|
-
|
218
|
-
@input.string = "\n \n\t\nabcd CAPABILITY\n"
|
219
|
-
assert_equal(%w[ abcd CAPABILITY ], @reader.read_command)
|
220
|
-
|
221
|
-
@input.string = "abcd OK CAPABILITY completed\n"
|
222
|
-
assert_equal(%w[ abcd OK CAPABILITY completed ], @reader.read_command)
|
223
|
-
|
224
|
-
@input.string = "A003 STORE 2:4 +FLAGS (\\Deleted)\n"
|
225
|
-
assert_equal([ 'A003', 'STORE', '2:4', '+FLAGS', [ :group, '\Deleted' ] ], @reader.read_command)
|
226
|
-
|
227
|
-
@input.string = "abcd SEARCH (OR (FROM foo) (FROM bar))\n"
|
228
|
-
assert_equal([ 'abcd', 'SEARCH',
|
229
|
-
[ :group,
|
230
|
-
'OR',
|
231
|
-
[ :group, 'FROM', 'foo' ],
|
232
|
-
[ :group, 'FROM', 'bar' ]
|
233
|
-
]
|
234
|
-
],
|
235
|
-
@reader.read_command)
|
236
|
-
|
237
|
-
@input.string = "abcd SEARCH SUBJECT \"NIL\"\n"
|
238
|
-
assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', 'NIL' ], @reader.read_command)
|
239
|
-
|
240
|
-
@input.string = "abcd SEARCH SUBJECT \"(\"\n"
|
241
|
-
assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', '(' ], @reader.read_command)
|
242
|
-
|
243
|
-
@input.string = "abcd SEARCH SUBJECT \")\"\n"
|
244
|
-
assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', ')' ], @reader.read_command)
|
245
|
-
|
246
|
-
@input.string = "abcd SEARCH SUBJECT \"[\"\n"
|
247
|
-
assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', '[' ], @reader.read_command)
|
248
|
-
|
249
|
-
@input.string = "abcd SEARCH SUBJECT \"]\"\n"
|
250
|
-
assert_equal([ 'abcd', 'SEARCH', 'SUBJECT', ']' ], @reader.read_command)
|
251
|
-
|
252
|
-
@input.string = "A654 FETCH 2:4 (BODY)\n"
|
253
|
-
assert_equal([ 'A654', 'FETCH', '2:4', [ :group, 'BODY' ] ], @reader.read_command)
|
254
|
-
|
255
|
-
@input.string = "A654 FETCH 2:4 (BODY[])\n"
|
256
|
-
assert_equal([ 'A654', 'FETCH', '2:4', [
|
257
|
-
:group, [
|
258
|
-
:body,
|
259
|
-
RIMS::Protocol.body(symbol: 'BODY',
|
260
|
-
section: '',
|
261
|
-
section_list: [])
|
262
|
-
]
|
263
|
-
]
|
264
|
-
], @reader.read_command)
|
265
|
-
|
266
|
-
@input.string = "A654 FETCH 2:4 (FLAGS BODY.PEEK[HEADER.FIELDS (DATE FROM)]<0.1500>)\n"
|
267
|
-
assert_equal([ 'A654', 'FETCH', '2:4',
|
268
|
-
[ :group,
|
269
|
-
'FLAGS',
|
270
|
-
[ :body,
|
271
|
-
RIMS::Protocol.body(symbol: 'BODY',
|
272
|
-
option: 'PEEK',
|
273
|
-
section: 'HEADER.FIELDS (DATE FROM)',
|
274
|
-
section_list: [ 'HEADER.FIELDS', [ :group, 'DATE', 'FROM' ] ],
|
275
|
-
partial_origin: 0,
|
276
|
-
partial_size: 1500)
|
277
|
-
]
|
278
|
-
]
|
279
|
-
],
|
280
|
-
@reader.read_command)
|
281
|
-
|
282
|
-
@input.string = "A654 APPEND saved-messages \"body[]\"\n"
|
283
|
-
assert_equal([ 'A654', 'APPEND', 'saved-messages', 'body[]' ], @reader.read_command)
|
284
|
-
|
285
|
-
assert_equal('', @output.string)
|
286
|
-
|
287
|
-
literal = <<-'EOF'
|
288
|
-
Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
|
289
|
-
From: Fred Foobar <foobar@Blurdybloop.COM>
|
290
|
-
Subject: afternoon meeting
|
291
|
-
To: mooch@owatagu.siam.edu
|
292
|
-
Message-Id: <B27397-0100000@Blurdybloop.COM>
|
293
|
-
MIME-Version: 1.0
|
294
|
-
Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
|
295
|
-
|
296
|
-
Hello Joe, do you think we can meet at 3:30 tomorrow?
|
297
|
-
EOF
|
298
|
-
|
299
|
-
@input.string = "A003 APPEND saved-messages (\\Seen) {#{literal.bytesize}}\n" + literal + "\n"
|
300
|
-
@output.string = ''
|
301
|
-
assert_equal([ 'A003', 'APPEND', 'saved-messages', [ :group, '\Seen' ], literal ], @reader.read_command)
|
302
|
-
|
303
|
-
cmd_cont_req = @output.string.each_line
|
304
|
-
assert_match(/^\+ /, cmd_cont_req.next)
|
305
|
-
assert_raise(StopIteration) { cmd_cont_req.next }
|
306
|
-
|
212
|
+
data('*' => %w[ * * ],
|
213
|
+
'+' => %w[ + + ],
|
214
|
+
'not_string' => [ '{123}', [ :literal, 123 ] ])
|
215
|
+
def test_read_command_invalid_tag_error(data)
|
216
|
+
invalid_tag, parsed_tag = data
|
217
|
+
@input.string = invalid_tag
|
218
|
+
error = assert_raise(RIMS::SyntaxError) { @reader.read_command }
|
219
|
+
assert_match(/invalid command tag/, error.message)
|
220
|
+
assert_include(error.message, parsed_tag.to_s)
|
221
|
+
end
|
307
222
|
|
308
|
-
|
309
|
-
|
223
|
+
def test_read_command_line_too_long_error
|
224
|
+
line = 'X001 x'
|
225
|
+
while (line.bytesize < LINE_LENGTH_LIMIT)
|
226
|
+
line << 'x'
|
227
|
+
end
|
228
|
+
line << "x\r\n"
|
229
|
+
assert_operator(line.bytesize, :>, LINE_LENGTH_LIMIT)
|
230
|
+
|
231
|
+
@input.string = line
|
232
|
+
assert_raise(RIMS::LineTooLongError) { @reader.read_command }
|
233
|
+
assert_equal("x\r\n", @input.read, 'not read end of line')
|
234
|
+
end
|
310
235
|
|
311
|
-
|
312
|
-
|
236
|
+
def test_read_command_literal_size_too_large_error
|
237
|
+
@input.string = "A001 APPEND saved-messages #{literal('x' * (LITERAL_SIZE_LIMIT + 1))}\n"
|
238
|
+
assert_raise(RIMS::LiteralSizeTooLargeError) { @reader.read_command }
|
239
|
+
assert_equal('x' * (LITERAL_SIZE_LIMIT + 1) + "\n", @input.read, 'not read literal')
|
240
|
+
end
|
313
241
|
|
314
|
-
|
315
|
-
@
|
316
|
-
|
242
|
+
def test_read_command_command_size_too_large_error_by_line
|
243
|
+
@input.string = "A001 APPEND #{literal('x' * LITERAL_SIZE_LIMIT)} #{'y' * (LINE_LENGTH_LIMIT - 2)}\n"
|
244
|
+
assert_raise(RIMS::CommandSizeTooLargeError) { @reader.read_command }
|
245
|
+
assert_equal('', @input.read, 'read whole a line')
|
246
|
+
end
|
317
247
|
|
318
|
-
|
319
|
-
|
320
|
-
assert_raise(
|
248
|
+
def test_read_command_command_size_too_large_error_by_literal
|
249
|
+
@input.string = "A001 APPEND #{literal('x' * LITERAL_SIZE_LIMIT)} #{literal('y' * LITERAL_SIZE_LIMIT)}\n"
|
250
|
+
assert_raise(RIMS::CommandSizeTooLargeError) { @reader.read_command }
|
251
|
+
assert_equal('y' * (LITERAL_SIZE_LIMIT) + "\n", @input.read, 'not read literal')
|
252
|
+
end
|
253
|
+
end
|
321
254
|
|
322
|
-
|
255
|
+
class ProtocolRequestReaderClassMethodTest < Test::Unit::TestCase
|
256
|
+
data('empty' => [ [], '' ],
|
257
|
+
'tagged_command' => [ %w[ abcd CAPABILITY ], 'abcd CAPABILITY' ],
|
258
|
+
'tagged_response' => [ %w[ abcd OK CAPABILITY completed ], 'abcd OK CAPABILITY completed' ],
|
259
|
+
'untagged_response' => [ %w[ * CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4 ], '* CAPABILITY IMAP4rev1 AUTH=KERBEROS_V4' ],
|
260
|
+
'untagged_exists' => [ %w[ * 172 EXISTS ], '* 172 EXISTS' ],
|
261
|
+
'untagged_unseen' => [
|
262
|
+
[ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ],
|
263
|
+
'* OK [UNSEEN 12] Message 12 is first unseen'
|
264
|
+
],
|
265
|
+
'untagged_flags' => [
|
266
|
+
[ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ],
|
267
|
+
'* FLAGS (\Answered \Flagged \Deleted \Seen \Draft)'
|
268
|
+
],
|
269
|
+
'untagged_permanentflags' => [
|
270
|
+
[ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ],
|
271
|
+
'* OK [PERMANENTFLAGS (\Deleted \Seen \*)] Limited'
|
272
|
+
],
|
273
|
+
'list_empty_reference' => [
|
274
|
+
[ 'A82', 'LIST', '', '*' ],
|
275
|
+
'A82 LIST "" *'
|
276
|
+
],
|
277
|
+
'untagged_list_delimiter' => [
|
278
|
+
[ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo' ],
|
279
|
+
'* LIST (\Noselect) "/" foo'
|
280
|
+
],
|
281
|
+
'untagged_list_quoted_name' => [
|
282
|
+
[ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, '/', 'foo [bar] (baz)' ],
|
283
|
+
'* LIST (\Noselect) "/" "foo [bar] (baz)"'
|
284
|
+
],
|
285
|
+
'untagged_list_nil_delimiter' => [
|
286
|
+
[ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ],
|
287
|
+
'* LIST (\Noselect) NIL ""'
|
288
|
+
],
|
289
|
+
'fetch_body_command' => [
|
290
|
+
[ 'A654', 'FETCH', '2:4',
|
291
|
+
'('.intern,
|
292
|
+
[ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
|
293
|
+
')'.intern
|
294
|
+
],
|
295
|
+
'A654 FETCH 2:4 (BODY[])'
|
296
|
+
],
|
297
|
+
'append_literal' => [
|
298
|
+
[ 'A003', 'APPEND', 'saved-messages', '('.intern, '\Seen', ')'.intern, [ :literal, 310 ] ],
|
299
|
+
'A003 APPEND saved-messages (\Seen) {310}'
|
300
|
+
])
|
301
|
+
def test_scan(data)
|
302
|
+
expected_atom_list, line = data
|
303
|
+
assert_equal(expected_atom_list, RIMS::Protocol::RequestReader.scan(line))
|
304
|
+
end
|
323
305
|
|
324
|
-
|
325
|
-
|
326
|
-
|
306
|
+
data('empty' => [ [], [] ],
|
307
|
+
'tagged_command' => [ %w[ abcd CAPABILITY ], %w[ abcd CAPABILITY ] ],
|
308
|
+
'tagged_response' => [ %w[ abcd OK CAPABILITY completed ], %w[ abcd OK CAPABILITY completed ] ],
|
309
|
+
'untagged_unseen' => [
|
310
|
+
[ '*', 'OK', [ :block, 'UNSEEN', '12' ], 'Message', '12', 'is', 'first', 'unseen' ],
|
311
|
+
[ '*', 'OK', '['.intern, 'UNSEEN', '12', ']'.intern, 'Message', '12', 'is', 'first', 'unseen' ]
|
312
|
+
],
|
313
|
+
'untagged_flags' =>[
|
314
|
+
[ '*', 'FLAGS', [ :group, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft' ] ],
|
315
|
+
[ '*', 'FLAGS', '('.intern, '\Answered', '\Flagged', '\Deleted', '\Seen', '\Draft', ')'.intern ]
|
316
|
+
],
|
317
|
+
'untagged_permanentflags' => [
|
318
|
+
[ '*', 'OK', [ :block, 'PERMANENTFLAGS', [ :group, '\Deleted', '\Seen', '\*' ] ], 'Limited' ],
|
319
|
+
[ '*', 'OK', '['.intern, 'PERMANENTFLAGS', '('.intern, '\Deleted', '\Seen', '\*', ')'.intern, ']'.intern, 'Limited' ]
|
320
|
+
],
|
321
|
+
'untagged_list_nil_delimiter' => [
|
322
|
+
[ '*', 'LIST', [ :group, '\Noselect' ], :NIL, '' ],
|
323
|
+
[ '*', 'LIST', '('.intern, '\Noselect', ')'.intern, :NIL, '' ]
|
324
|
+
],
|
325
|
+
'fetch_body_command' => [
|
326
|
+
[ 'A654', 'FETCH', '2:4',
|
327
|
+
[ :group,
|
328
|
+
[ :body,
|
329
|
+
RIMS::Protocol.body(symbol: 'BODY',
|
330
|
+
section: '',
|
331
|
+
section_list: [])
|
332
|
+
]
|
333
|
+
]
|
334
|
+
],
|
335
|
+
[ 'A654', 'FETCH', '2:4',
|
336
|
+
'('.intern,
|
337
|
+
[ :body, RIMS::Protocol.body(symbol: 'BODY', section: '') ],
|
338
|
+
')'.intern
|
339
|
+
]
|
340
|
+
],
|
341
|
+
'append_command' => [
|
342
|
+
[ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ],
|
343
|
+
[ 'A003', 'APPEND', 'saved-messages', "foo\nbody[]\nbar\n" ]
|
344
|
+
])
|
345
|
+
def test_parse(data)
|
346
|
+
expected_atom_list, input_atom_list = data
|
347
|
+
assert_equal(expected_atom_list, RIMS::Protocol::RequestReader.parse(input_atom_list))
|
348
|
+
end
|
327
349
|
|
328
|
-
|
329
|
-
|
330
|
-
|
350
|
+
data('unclosed_square_bracket' => [ '*', 'OK', '['.intern, 'UNSEEN', '12' ],
|
351
|
+
'unclosed_parenthesis' => [ '*', 'LIST', '('.intern, '\Noselect' ])
|
352
|
+
def test_parse_not_found_a_terminator_error(data)
|
353
|
+
input_atom_list = data
|
354
|
+
error = assert_raise(RIMS::SyntaxError) { RIMS::Protocol::RequestReader.parse(input_atom_list) }
|
355
|
+
assert_match(/not found a terminator/, error.message)
|
331
356
|
end
|
332
357
|
end
|
333
358
|
end
|