diff options
-rw-r--r-- | lib/net/imap.rb | 524 | ||||
-rw-r--r-- | test/net/imap/test_imap.rb | 69 | ||||
-rw-r--r-- | test/net/imap/test_imap_response_parser.rb | 78 |
3 files changed, 593 insertions, 78 deletions
@@ -201,7 +201,7 @@ module Net # Unicode", RFC 2152, May 1997. # class IMAP < Protocol - VERSION = "0.1.1" include MonitorMixin if defined?(OpenSSL::SSL) @@ -304,6 +304,16 @@ module Net @@authenticators[auth_type] = authenticator end # The default port for IMAP connections, port 143 def self.default_port return PORT @@ -365,6 +375,30 @@ module Net end end # Sends a NOOP command to the server. It does nothing. def noop send_command("NOOP") @@ -408,7 +442,7 @@ module Net # the form "AUTH=LOGIN" or "AUTH=CRAM-MD5". # # Authentication is done using the appropriate authenticator object: - # see @@authenticators for more information on plugging in your own # authenticator. # # For example: @@ -417,12 +451,7 @@ module Net # # A Net::IMAP::NoResponseError is raised if authentication fails. def authenticate(auth_type, *args) - auth_type = auth_type.upcase - unless @@authenticators.has_key?(auth_type) - raise ArgumentError, - format('unknown auth type - "%s"', auth_type) - end - authenticator = @@authenticators[auth_type].new(*args) send_command("AUTHENTICATE", auth_type) do |resp| if resp.instance_of?(ContinuationRequest) data = authenticator.process(resp.data.text.unpack("m")[0]) @@ -552,6 +581,60 @@ module Net end end # Sends a XLIST command, and returns a subset of names from # the complete set of all names available to the client. # +refname+ provides a context (for instance, a base directory @@ -1656,6 +1739,74 @@ module Net end end # Common validators of number and nz_number types module NumValidator # :nodoc class << self @@ -1747,6 +1898,18 @@ module Net # raw_data:: Returns the raw data string. UntaggedResponse = Struct.new(:name, :data, :raw_data) # Net::IMAP::TaggedResponse represents tagged responses. # # The server completion result response indicates the success or @@ -1774,8 +1937,7 @@ module Net # Net::IMAP::ResponseText represents texts of responses. # The text may be prefixed by the response code. # - # resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) - # ;; text SHOULD NOT begin with "[" or "=" # # ==== Fields: # @@ -1787,12 +1949,15 @@ module Net # Net::IMAP::ResponseCode represents response codes. # - # resp_text_code ::= "ALERT" / "PARSE" / - # "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / - # "UIDVALIDITY" SPACE nz_number / - # "UNSEEN" SPACE nz_number / - # atom [SPACE 1*<any TEXT_CHAR except "]">] # # ==== Fields: # @@ -1872,6 +2037,39 @@ module Net # MailboxACLItem = Struct.new(:user, :rights, :mailbox) # Net::IMAP::StatusData represents the contents of the STATUS response. # # ==== Fields: @@ -2291,8 +2489,12 @@ module Net return response_cond when /\A(?:FLAGS)\z/ni return flags_response when /\A(?:LIST|LSUB|XLIST)\z/ni return list_response when /\A(?:QUOTA)\z/ni return getquota_response when /\A(?:QUOTAROOT)\z/ni @@ -2307,6 +2509,8 @@ module Net return status_response when /\A(?:CAPABILITY)\z/ni return capability_response else return text_response end @@ -2316,7 +2520,7 @@ module Net end def response_tagged - tag = atom match(T_SPACE) token = match(T_ATOM) name = token.value.upcase @@ -2876,14 +3080,18 @@ module Net return name, modseq end def text_response token = match(T_ATOM) name = token.value.upcase match(T_SPACE) - @lex_state = EXPR_TEXT - token = match(T_TEXT) - @lex_state = EXPR_BEG - return UntaggedResponse.new(name, token.value) end def flags_response @@ -3114,11 +3322,15 @@ module Net token = match(T_ATOM) name = token.value.upcase match(T_SPACE) data = [] while true token = lookahead case token.symbol - when T_CRLF break when T_SPACE shift_token @@ -3126,30 +3338,142 @@ module Net end data.push(atom.upcase) end return UntaggedResponse.new(name, data, @str) end - def resp_text - @lex_state = EXPR_RTEXT token = lookahead - if token.symbol == T_LBRA - code = resp_text_code else - code = nil end - token = match(T_TEXT) - @lex_state = EXPR_BEG - return ResponseText.new(code, token.value) end def resp_text_code - @lex_state = EXPR_BEG - match(T_LBRA) token = match(T_ATOM) name = token.value.upcase case name when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n result = ResponseCode.new(name, nil) when /\A(?:PERMANENTFLAGS)\z/n match(T_SPACE) result = ResponseCode.new(name, flag_list) @@ -3160,19 +3484,28 @@ module Net token = lookahead if token.symbol == T_SPACE shift_token - @lex_state = EXPR_CTEXT - token = match(T_TEXT) - @lex_state = EXPR_BEG result = ResponseCode.new(name, token.value) else result = ResponseCode.new(name, nil) end end - match(T_RBRA) - @lex_state = EXPR_RTEXT return result end def address_list token = lookahead if token.symbol == T_NIL @@ -3269,7 +3602,7 @@ module Net if string_token?(token) return string else - return atom end end @@ -3299,34 +3632,49 @@ module Net return token.value.upcase end - def atom - result = String.new - while true - token = lookahead - if atom_token?(token) - result.concat(token.value) - shift_token - else - if result.empty? - parse_error("unexpected token %s", token.symbol) - else - return result - end - end - end - end - ATOM_TOKENS = [ T_ATOM, T_NUMBER, T_NIL, T_LBRA, - T_RBRA, T_PLUS ] - def atom_token?(token) - return ATOM_TOKENS.include?(token.symbol) end def number @@ -3344,22 +3692,62 @@ module Net return nil end - def match(*args) token = lookahead - unless args.include?(token.symbol) - parse_error('unexpected token %s (expected %s)', - token.symbol.id2name, - args.collect {|i| i.id2name}.join(" or ")) end - shift_token - return token end def lookahead - unless @token - @token = next_token - end - return @token end def shift_token @@ -578,23 +578,23 @@ class IMAPTest < Test::Unit::TestCase begin imap = Net::IMAP.new(server_addr, :port => port) assert_raise(Net::IMAP::DataFormatError) do - imap.send(:send_command, "TEST", -1) end - imap.send(:send_command, "TEST", 0) - imap.send(:send_command, "TEST", 4294967295) assert_raise(Net::IMAP::DataFormatError) do - imap.send(:send_command, "TEST", 4294967296) end assert_raise(Net::IMAP::DataFormatError) do - imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(-1)) end assert_raise(Net::IMAP::DataFormatError) do - imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(0)) end - imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(1)) - imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967295)) assert_raise(Net::IMAP::DataFormatError) do - imap.send(:send_command, "TEST", Net::IMAP::MessageSet.new(4294967296)) end imap.logout ensure @@ -628,7 +628,7 @@ class IMAPTest < Test::Unit::TestCase end begin imap = Net::IMAP.new(server_addr, :port => port) - imap.send(:send_command, "TEST", ["\xDE\xAD\xBE\xEF".b]) assert_equal(2, requests.length) assert_equal("RUBY0001 TEST ({4}\r\n", requests[0]) assert_equal("\xDE\xAD\xBE\xEF".b, literal) @@ -753,6 +753,55 @@ EOF end end private def imaps_test @@ -234,6 +234,27 @@ EOF response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN \r\n") assert_equal("CAPABILITY", response.name) assert_equal("AUTH=PLAIN", response.data.last) end def test_mixed_boundary @@ -301,6 +322,22 @@ EOF assert_equal(12345, response.data.attr["MODSEQ"]) end def test_continuation_request_without_response_text parser = Net::IMAP::ResponseParser.new response = parser.parse("+\r\n") @@ -308,4 +345,45 @@ EOF assert_equal(nil, response.data.code) assert_equal("", response.data.text) end end |