class RIMS::Protocol::Decoder
Constants
- BulkResponse
- Engine
alias
- IMAP_CMDs
- UID_CMDs
Attributes
next_decoder[R]
Public Class Methods
decode_delivery_target_mailbox(encoded_mbox_name)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 2035 def Decoder.decode_delivery_target_mailbox(encoded_mbox_name) encode_type, base64_username, mbox_name = encoded_mbox_name.split(' ', 3) if (encode_type != 'b64user-mbox') then raise SyntaxError, "unknown mailbox encode type: #{encode_type}" end return Protocol.decode_base64(base64_username), mbox_name end
encode_delivery_target_mailbox(username, mbox_name)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 2031 def Decoder.encode_delivery_target_mailbox(username, mbox_name) "b64user-mbox #{Protocol.encode_base64(username)} #{mbox_name}" end
imap_command_normalize(name)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 20 def self.imap_command_normalize(name) name.upcase end
logging_error_chain(error, logger)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 24 def self.logging_error_chain(error, logger) Error.trace_error_chain(error) do |exception| if (logger.debug?) then Error.optional_data(exception) do |error, data| logger.debug("error message: #{error.message} (#{error.class})") for name, value in data logger.debug("error data [#{name}]: #{value.pretty_inspect}") end end end logger.error(exception) end end
make_engine_and_recovery_if_needed(drb_services, username, logger: Logger.new(STDOUT)) { |response| ... }
click to toggle source
# File lib/rims/protocol/decoder.rb, line 293 def make_engine_and_recovery_if_needed(drb_services, username, logger: Logger.new(STDOUT)) unique_user_id = Authentication.unique_user_id(username) logger.debug("unique user ID: #{username} -> #{unique_user_id}") if logger.debug? logger.info("open mail store: #{unique_user_id} [ #{username} ]") engine = drb_services[:engine, unique_user_id] begin engine.recovery_if_needed(username) {|response| yield(response) } rescue engine.destroy raise end engine end
new(auth, logger)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 201 def initialize(auth, logger) @auth = auth @logger = logger @next_decoder = self end
new_decoder(*args, **opts)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 13 def self.new_decoder(*args, **opts) InitialDecoder.new(*args, **opts) end
repl(decoder, limits, input, output, logger)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 38 def self.repl(decoder, limits, input, output, logger) output_write = lambda{|data| begin if (data == :flush) then output.flush else logger.debug("response data: #{Protocol.io_data_log(data)}") if logger.debug? output << data end rescue logger.error('response write error.') logging_error_chain($!, logger) raise end } server_output_write = lambda{|res| for data in res output_write.call(data) end output.flush nil } response_write = lambda{|response| output_write.call(response) output.flush logger.info("server response: #{response.strip}") } apply_imap_command = lambda{|name, *args, uid: false| last_line = nil if (uid) then decoder.__send__(name, *args, uid: true) {|response| output_write.call(response) last_line = response if (response.is_a? String) } else decoder.__send__(name, *args) {|response| output_write.call(response) last_line = response if (response.is_a? String) } end output.flush logger.info("server response: #{last_line.strip}") if last_line } apply_imap_command.call(:ok_greeting) conn_timer = ConnectionTimer.new(limits, input.to_io) request_reader = decoder.make_requrest_reader(input, output) input_gets = request_reader.method(:gets) begin until (conn_timer.command_wait_timeout?) conn_timer.command_wait or break begin atom_list = request_reader.read_command rescue LineTooLongError raise rescue LiteralSizeTooLargeError logger.error('literal size too large error.') logging_error_chain($!, logger) response_write.call("#{request_reader.command_tag || '*'} BAD literal size too large\r\n") next rescue CommandSizeTooLargeError logger.error('command size too large error.') logging_error_chain($!, logger) response_write.call("#{request_reader.command_tag || '*'} BAD command size too large\r\n") next rescue logger.error('invalid client command.') logging_error_chain($!, logger) response_write.call("#{request_reader.command_tag || '*'} BAD client command syntax error\r\n") next end break unless atom_list tag, command, *opt_args = atom_list normalized_command = imap_command_normalize(command) logger.info("client command: #{tag} #{command}") if (logger.debug?) then case (normalized_command) when 'LOGIN' log_opt_args = opt_args.dup log_opt_args[-1] = '********' when 'AUTHENTICATE' if (opt_args[1]) then log_opt_args = opt_args.dup log_opt_args[1] = '********' else log_opt_args = opt_args end else log_opt_args = opt_args end logger.debug("client command parameter: #{log_opt_args.inspect}") end begin if (name = IMAP_CMDs[normalized_command]) then case (name) when :uid unless (opt_args.empty?) then uid_command, *uid_args = opt_args logger.info("uid command: #{uid_command}") logger.debug("uid parameter: #{uid_args}") if logger.debug? if (uid_name = UID_CMDs[imap_command_normalize(uid_command)]) then apply_imap_command.call(uid_name, tag, *uid_args, uid: true) else logger.error("unknown uid command: #{uid_command}") response_write.call("#{tag} BAD unknown uid command\r\n") end else logger.error('empty uid parameter.') response_write.call("#{tag} BAD empty uid parameter\r\n") end when :authenticate apply_imap_command.call(:authenticate, tag, input_gets, server_output_write, *opt_args) when :idle apply_imap_command.call(:idle, tag, input_gets, server_output_write, conn_timer, *opt_args) else apply_imap_command.call(name, tag, *opt_args) end else logger.error("unknown command: #{command}") response_write.call("#{tag} BAD unknown command\r\n") end rescue LineTooLongError raise rescue logger.error('unexpected error.') logging_error_chain($!, logger) response_write.call("#{tag} BAD unexpected error\r\n") end if (normalized_command == 'LOGOUT') then break end decoder = decoder.next_decoder end rescue LineTooLongError logger.error('line too long error.') logging_error_chain($!, logger) response_write.call("* BAD line too long\r\n") response_write.call("* BYE server autologout: connection terminated\r\n") else if (conn_timer.command_wait_timeout?) then if (limits.command_wait_timeout_seconds > 0) then response_write.call("* BYE server autologout: idle for too long\r\n") else response_write.call("* BYE server autologout: shutdown\r\n") end end end nil ensure # don't forget to clean up if the next decoder has been generated decoder.next_decoder.cleanup end
Private Class Methods
imap_command(name)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 282 def imap_command(name) should_be_imap_command(name) orig_name = "_#{name}".to_sym alias_method orig_name, name define_method name, lambda{|tag, *args, **kw_args, &block| guard_error(orig_name, tag, *args, **kw_args, &block) } name.to_sym end
kw_params(method)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 246 def kw_params(method) params = method.parameters params.find_all{|arg_type, arg_name| case (arg_type) when :key, :keyreq true else false end }.map{|arg_type, arg_name| arg_name } end
should_be_imap_command(name)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 261 def should_be_imap_command(name) cmd = to_imap_command(name) unless (IMAP_CMDs.key? cmd) then raise ArgumentError, "not an IMAP command: #{name}" end method = instance_method(name) if (UID_CMDs.key? cmd) then unless (kw_params(method).include? :uid) then raise ArgumentError, "not defined `uid' keyword parameter: #{name}" end else if (kw_params(method).include? :uid) then raise ArgumentError, "not allowed `uid' keyword parameter: #{name}" end end nil end
to_imap_command(name)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 241 def to_imap_command(name) imap_command_normalize(name.to_s) end
Public Instance Methods
capability(tag) { |"* CAPABILITY #{join(' ')}\r\n"| ... }
click to toggle source
# File lib/rims/protocol/decoder.rb, line 324 def capability(tag) capability_list = %w[ IMAP4rev1 UIDPLUS IDLE ] capability_list += @auth.capability.map{|auth_capability| "AUTH=#{auth_capability}" } yield("* CAPABILITY #{capability_list.join(' ')}\r\n") yield("#{tag} OK CAPABILITY completed\r\n") end
ok_greeting() { |"* OK RIMS v#{VERSION} IMAP4rev1 service ready.\r\n"| ... }
click to toggle source
# File lib/rims/protocol/decoder.rb, line 317 def ok_greeting yield("* OK RIMS v#{VERSION} IMAP4rev1 service ready.\r\n") end
Private Instance Methods
guard_error(imap_command, tag, *args, **kw_args) { |"#{tag} BAD client command syntax error\r\n"| ... }
click to toggle source
# File lib/rims/protocol/decoder.rb, line 214 def guard_error(imap_command, tag, *args, **kw_args, &block) begin if (kw_args.empty?) then __send__(imap_command, tag, *args, &block) else __send__(imap_command, tag, *args, **kw_args, &block) end rescue LineTooLongError raise rescue SyntaxError @logger.error('client command syntax error.') logging_error_chain($!) yield("#{tag} BAD client command syntax error\r\n") rescue ArgumentError @logger.error('invalid command parameter.') logging_error_chain($!) yield("#{tag} BAD invalid command parameter\r\n") rescue raise if ($!.class.name =~ /AssertionFailedError/) @logger.error('internal server error.') logging_error_chain($!) yield("#{tag} BAD internal server error\r\n") end end
logging_error_chain(error)
click to toggle source
# File lib/rims/protocol/decoder.rb, line 209 def logging_error_chain(error) self.class.logging_error_chain(error, @logger) end
make_logout_response(tag) { |"* BYE server logout\r\n"| ... }
click to toggle source
# File lib/rims/protocol/decoder.rb, line 311 def make_logout_response(tag) yield("* BYE server logout\r\n") yield("#{tag} OK LOGOUT completed\r\n") end