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