rims 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/CHANGELOG.md +96 -0
- data/ChangeLog +109 -0
- data/Rakefile +8 -4
- data/lib/rims.rb +31 -29
- data/lib/rims/channel.rb +35 -10
- data/lib/rims/cmd.rb +153 -31
- data/lib/rims/error.rb +15 -0
- data/lib/rims/protocol.rb +28 -7
- data/lib/rims/protocol/decoder.rb +763 -469
- data/lib/rims/protocol/parser.rb +316 -181
- data/lib/rims/service.rb +190 -57
- data/lib/rims/test.rb +46 -17
- data/lib/rims/version.rb +1 -1
- data/load_test/Rakefile +3 -1
- data/rims.gemspec +14 -12
- data/test/cmd/test_command.rb +135 -23
- 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 +733 -75
- data/test/test_protocol_fetch.rb +384 -189
- data/test/test_protocol_request.rb +305 -280
- data/test/test_protocol_search.rb +771 -892
- data/test/test_service.rb +165 -0
- metadata +34 -8
- data/lib/rims/rfc822.rb +0 -463
- data/test/test_rfc822.rb +0 -696
@@ -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
|