diff options
Diffstat (limited to 'lib/feed2imap')
-rw-r--r-- | lib/feed2imap/rubyimap.rb | 617 |
1 files changed, 371 insertions, 246 deletions
diff --git a/lib/feed2imap/rubyimap.rb b/lib/feed2imap/rubyimap.rb index 49bd8a3..dc86361 100644 --- a/lib/feed2imap/rubyimap.rb +++ b/lib/feed2imap/rubyimap.rb @@ -1,8 +1,7 @@ # File fetched from # http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/net/imap.rb?view=log -# Current rev: 13244 +# Current rev: 22784 ############################################################################ - # # = net/imap.rb # @@ -14,9 +13,10 @@ # Documentation: Shugo Maeda, with RDoc conversion and overview by William # Webber. # -# See Net::IMAP for documentation. +# See Net::IMAP for documentation. # + require "socket" require "monitor" require "digest/md5" @@ -49,12 +49,12 @@ module Net # read-only access) #examine(). Once the client has successfully # selected a mailbox, they enter _selected_ state, and that # mailbox becomes the _current_ mailbox, on which mail-item - # related commands implicitly operate. + # related commands implicitly operate. # # Messages have two sorts of identifiers: message sequence - # numbers, and UIDs. + # numbers, and UIDs. # - # Message sequence numbers number messages within a mail box + # Message sequence numbers number messages within a mail box # from 1 up to the number of items in the mail box. If new # message arrives during a session, it receives a sequence # number equal to the new size of the mail box. If messages @@ -62,7 +62,7 @@ module Net # sequence numbers "shuffled down" to fill the gaps. # # UIDs, on the other hand, are permanently guaranteed not to - # identify another message within the same mailbox, even if + # identify another message within the same mailbox, even if # the existing message is deleted. UIDs are required to # be assigned in ascending (but not necessarily sequential) # order within a mailbox; this means that if a non-IMAP client @@ -95,11 +95,11 @@ module Net # imap.store(message_id, "+FLAGS", [:Deleted]) # end # imap.expunge - # + # # == Thread Safety # # Net::IMAP supports concurrent threads. For example, - # + # # imap = Net::IMAP.new("imap.foo.net", "imap2") # imap.authenticate("cram-md5", "bar", "password") # imap.select("inbox") @@ -107,7 +107,7 @@ module Net # search_result = imap.search(["BODY", "hello"]) # fetch_result = fetch_thread.value # imap.disconnect - # + # # This script invokes the FETCH command and the SEARCH command concurrently. # # == Errors @@ -117,9 +117,9 @@ module Net # # NO:: the attempted command could not be successfully completed. For # instance, the username/password used for logging in are incorrect; - # the selected mailbox does not exists; etc. + # the selected mailbox does not exists; etc. # - # BAD:: the request from the client does not follow the server's + # BAD:: the request from the client does not follow the server's # understanding of the IMAP protocol. This includes attempting # commands from the wrong client state; for instance, attempting # to perform a SEARCH command without having SELECTed a current @@ -151,8 +151,8 @@ module Net # # Finally, a Net::IMAP::DataFormatError is thrown if low-level data # is found to be in an incorrect format (for instance, when converting - # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is - # thrown if a server response is non-parseable. + # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is + # thrown if a server response is non-parseable. # # # == References @@ -278,10 +278,10 @@ module Net # is the type of authentication this authenticator supports # (for instance, "LOGIN"). The +authenticator+ is an object # which defines a process() method to handle authentication with - # the server. See Net::IMAP::LoginAuthenticator, + # the server. See Net::IMAP::LoginAuthenticator, # Net::IMAP::CramMD5Authenticator, and Net::IMAP::DigestMD5Authenticator # for examples. - # + # # # If +auth_type+ refers to an existing authenticator, it will be # replaced by the new one. @@ -291,10 +291,16 @@ module Net # Disconnects from the server. def disconnect - if SSL::SSLSocket === @sock - @sock.io.shutdown - else - @sock.shutdown + begin + begin + # try to call SSL::SSLSocket#io. + @sock.io.shutdown + rescue NoMethodError + # @sock is not an SSL::SSLSocket. + @sock.shutdown + end + rescue Errno::ENOTCONN + # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms. end @receiver_thread.join @sock.close @@ -312,7 +318,7 @@ module Net # # Note that the Net::IMAP class does not modify its # behaviour according to the capabilities of the server; - # it is up to the user of the class to ensure that + # it is up to the user of the class to ensure that # a certain capability is supported by a server before # using it. def capability @@ -334,19 +340,16 @@ module Net end # Sends a STARTTLS command to start TLS session. - def starttls(ctx = nil) - if @sock.kind_of?(OpenSSL::SSL::SSLSocket) - raise RuntimeError, "already using SSL" - end + def starttls(options = {}, verify = true) send_command("STARTTLS") do |resp| if resp.kind_of?(TaggedResponse) && resp.name == "OK" - if ctx - @sock = OpenSSL::SSL::SSLSocket.new(@sock, ctx) - else - @sock = OpenSSL::SSL::SSLSocket.new(@sock) + begin + # for backward compatibility + certs = options.to_str + options = create_ssl_params(certs, verify) + rescue NoMethodError end - @sock.sync_close = true - @sock.connect + start_tls_session(options) end end end @@ -356,7 +359,7 @@ module Net # the authentication mechanism to be used. Currently Net::IMAP # supports authentication mechanisms: # - # LOGIN:: login using cleartext user and password. + # LOGIN:: login using cleartext user and password. # CRAM-MD5:: login with cleartext user and encrypted password # (see [RFC-2195] for a full description). This # mechanism requires that the server have the user's @@ -404,7 +407,7 @@ module Net end # Sends a SELECT command to select a +mailbox+ so that messages - # in the +mailbox+ can be accessed. + # in the +mailbox+ can be accessed. # # After you have selected a mailbox, you may retrieve the # number of items in that mailbox from @responses["EXISTS"][-1], @@ -455,7 +458,7 @@ module Net # Sends a RENAME command to change the name of the +mailbox+ to # +newname+. # - # A Net::IMAP::NoResponseError is raised if a mailbox with the + # A Net::IMAP::NoResponseError is raised if a mailbox with the # name +mailbox+ cannot be renamed to +newname+ for whatever # reason; for instance, because +mailbox+ does not exist, or # because there is already a mailbox with the name +newname+. @@ -502,8 +505,8 @@ module Net # imap.create("foo/bar") # imap.create("foo/baz") # p imap.list("", "foo/%") - # #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\ - # #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\ + # #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, \\ + # #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, \\ # #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">] def list(refname, mailbox) synchronize do @@ -556,7 +559,7 @@ module Net # then that user will be stripped of any rights to that mailbox. # The IMAP ACL commands are described in [RFC-2086]. def setacl(mailbox, user, rights) - if rights.nil? + if rights.nil? send_command("SETACL", mailbox, user, "") else send_command("SETACL", mailbox, user, rights) @@ -575,7 +578,7 @@ module Net # Sends a LSUB command, and returns a subset of names from the set # of names that the user has declared as being "active" or - # "subscribed". +refname+ and +mailbox+ are interpreted as + # "subscribed". +refname+ and +mailbox+ are interpreted as # for #list(). # The return value is an array of +Net::IMAP::MailboxList+. def lsub(refname, mailbox) @@ -598,7 +601,7 @@ module Net # p imap.status("inbox", ["MESSAGES", "RECENT"]) # #=> {"RECENT"=>0, "MESSAGES"=>44} # - # A Net::IMAP::NoResponseError is raised if status values + # A Net::IMAP::NoResponseError is raised if status values # for +mailbox+ cannot be returned, for instance because it # does not exist. def status(mailbox, attr) @@ -609,9 +612,9 @@ module Net end # Sends a APPEND command to append the +message+ to the end of - # the +mailbox+. The optional +flags+ argument is an array of + # the +mailbox+. The optional +flags+ argument is an array of # flags to initially passing to the new message. The optional - # +date_time+ argument specifies the creation time to assign to the + # +date_time+ argument specifies the creation time to assign to the # new message; it defaults to the current time. # For example: # @@ -619,7 +622,7 @@ module Net # Subject: hello # From: shugo@ruby-lang.org # To: shugo@ruby-lang.org - # + # # hello world # EOF # @@ -638,7 +641,7 @@ module Net # Sends a CHECK command to request a checkpoint of the currently # selected mailbox. This performs implementation-specific - # housekeeping, for instance, reconciling the mailbox's + # housekeeping, for instance, reconciling the mailbox's # in-memory and on-disk state. def check send_command("CHECK") @@ -662,8 +665,8 @@ module Net # Sends a SEARCH command to search the mailbox for messages that # match the given searching criteria, and returns message sequence - # numbers. +keys+ can either be a string holding the entire - # search string, or a single-dimension array of search keywords and + # numbers. +keys+ can either be a string holding the entire + # search string, or a single-dimension array of search keywords and # arguments. The following are some common search criteria; # see [IMAP] section 6.4.4 for a full list. # @@ -687,7 +690,7 @@ module Net # # OR <search-key> <search-key>:: "or" two search keys together. # - # ON <date>:: messages with an internal date exactly equal to <date>, + # ON <date>:: messages with an internal date exactly equal to <date>, # which has a format similar to 8-Aug-2002. # # SINCE <date>:: messages with an internal date on or after <date>. @@ -695,7 +698,7 @@ module Net # SUBJECT <string>:: messages with <string> in their subject. # # TO <string>:: messages with <string> in their TO field. - # + # # For example: # # p imap.search(["SUBJECT", "hello", "NOT", "NEW"]) @@ -718,8 +721,8 @@ module Net # The return value is an array of Net::IMAP::FetchData. For example: # # p imap.fetch(6..8, "UID") - # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\ - # #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\ + # #=> [#<Net::IMAP::FetchData seqno=6, attr={"UID"=>98}>, \\ + # #<Net::IMAP::FetchData seqno=7, attr={"UID"=>99}>, \\ # #<Net::IMAP::FetchData seqno=8, attr={"UID"=>100}>] # p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]") # #=> [#<Net::IMAP::FetchData seqno=6, attr={"BODY[HEADER.FIELDS (SUBJECT)]"=>"Subject: test\r\n\r\n"}>] @@ -742,9 +745,9 @@ module Net end # Sends a STORE command to alter data associated with messages - # in the mailbox, in particular their flags. The +set+ parameter - # is a number or an array of numbers or a Range object. Each number - # is a message sequence number. +attr+ is the name of a data item + # in the mailbox, in particular their flags. The +set+ parameter + # is a number or an array of numbers or a Range object. Each number + # is a message sequence number. +attr+ is the name of a data item # to store: 'FLAGS' means to replace the message's flag list # with the provided one; '+FLAGS' means to add the provided flags; # and '-FLAGS' means to remove them. +flags+ is a list of flags. @@ -752,8 +755,8 @@ module Net # The return value is an array of Net::IMAP::FetchData. For example: # # p imap.store(6..8, "+FLAGS", [:Deleted]) - # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\ - # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\ + # #=> [#<Net::IMAP::FetchData seqno=6, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\ + # #<Net::IMAP::FetchData seqno=7, attr={"FLAGS"=>[:Seen, :Deleted]}>, \\ # #<Net::IMAP::FetchData seqno=8, attr={"FLAGS"=>[:Seen, :Deleted]}>] def store(set, attr, flags) return store_internal("STORE", set, attr, flags) @@ -795,9 +798,9 @@ module Net return sort_internal("UID SORT", sort_keys, search_keys, charset) end - # Adds a response handler. For example, to detect when + # Adds a response handler. For example, to detect when # the server sends us a new EXISTS response (which normally - # indicates new messages being added to the mail box), + # indicates new messages being added to the mail box), # you could add the following handler after selecting the # mailbox. # @@ -833,7 +836,7 @@ module Net return thread_internal("THREAD", algorithm, search_keys, charset) end - # As for #thread(), but returns unique identifiers instead of + # As for #thread(), but returns unique identifiers instead of # message sequence numbers. def uid_thread(algorithm, search_keys, charset) return thread_internal("UID THREAD", algorithm, search_keys, charset) @@ -859,37 +862,47 @@ module Net end base64.unpack("m")[0].unpack("n*").pack("U*") end - } + }.force_encoding("UTF-8") end # Encode a string from UTF-8 format to modified UTF-7. def self.encode_utf7(s) - return s.gsub(/(&)|([^\x20-\x25\x27-\x7e]+)/n) { |x| + return s.gsub(/(&)|([^\x20-\x25\x27-\x7e]+)/u) { if $1 "&-" else - base64 = [x.unpack("U*").pack("n*")].pack("m") + base64 = [$&.unpack("U*").pack("n*")].pack("m") "&" + base64.delete("=\n").tr("/", ",") + "-" end - } + }.force_encoding("ASCII-8BIT") end private CRLF = "\r\n" # :nodoc: PORT = 143 # :nodoc: + SSL_PORT = 993 # :nodoc: @@debug = false @@authenticators = {} + # call-seq: + # Net::IMAP.new(host, options = {}) + # # Creates a new Net::IMAP object and connects it to the specified - # +port+ (143 by default) on the named +host+. If +usessl+ is true, - # then an attempt will - # be made to use SSL (now TLS) to connect to the server. For this - # to work OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] - # extensions need to be installed. The +certs+ parameter indicates - # the path or file containing the CA cert of the server, and the - # +verify+ parameter is for the OpenSSL verification callback. + # +host+. + # + # +options+ is an option hash, each key of which is a symbol. + # + # The available options are: + # + # port:: port number (default value is 143 for imap, or 993 for imaps) + # ssl:: if options[:ssl] is true, then an attempt will be made + # to use SSL (now TLS) to connect to the server. For this to work + # OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] extensions need to + # be installed. + # if options[:ssl] is a hash, it's passed to + # OpenSSL::SSL::SSLContext#set_params as parameters. # # The most common errors are: # @@ -899,32 +912,30 @@ module Net # being dropped by an intervening firewall). # Errno::ENETUNREACH:: there is no route to that network. # SocketError:: hostname not known or other socket error. - # Net::IMAP::ByeResponseError:: we connected to the host, but they + # Net::IMAP::ByeResponseError:: we connected to the host, but they # immediately said goodbye to us. - def initialize(host, port = PORT, usessl = false, certs = nil, verify = false) + def initialize(host, port_or_options = {}, + usessl = false, certs = nil, verify = true) super() @host = host - @port = port + begin + options = port_or_options.to_hash + rescue NoMethodError + # for backward compatibility + options = {} + options[:port] = port_or_options + if usessl + options[:ssl] = create_ssl_params(certs, verify) + end + end + @port = options[:port] || (options[:ssl] ? SSL_PORT : PORT) @tag_prefix = "RUBY" @tagno = 0 @parser = ResponseParser.new - @sock = TCPSocket.open(host, port) - if usessl - unless defined?(OpenSSL) - raise "SSL extension not installed" - end + @sock = TCPSocket.open(@host, @port) + if options[:ssl] + start_tls_session(options[:ssl]) @usessl = true - - # verify the server. - context = SSLContext::new() - context.ca_file = certs if certs && FileTest::file?(certs) - context.ca_path = certs if certs && FileTest::directory?(certs) - context.verify_mode = VERIFY_PEER if verify - if defined?(VerifyCallbackProc) - context.verify_callback = VerifyCallbackProc - end - @sock = SSLSocket.new(@sock, context) - @sock.connect # start ssl session. else @usessl = false end @@ -935,6 +946,7 @@ module Net @continuation_request_arrival = new_cond @logout_command_tag = nil @debug_output_bol = true + @exception = nil @greeting = get_response if @greeting.name == "BYE" @@ -950,14 +962,24 @@ module Net def receive_responses while true + synchronize do + @exception = nil + end begin resp = get_response - rescue Exception - @sock.close - @client_thread.raise($!) + rescue Exception => e + synchronize do + @sock.close + @exception = e + end + break + end + unless resp + synchronize do + @exception = EOFError.new("end of file reached") + end break end - break unless resp begin synchronize do case resp @@ -975,7 +997,8 @@ module Net end if resp.name == "BYE" && @logout_command_tag.nil? @sock.close - raise ByeResponseError, resp.raw_data + @exception = ByeResponseError.new(resp.raw_data) + break end when ContinuationRequest @continuation_request_arrival.signal @@ -984,14 +1007,23 @@ module Net handler.call(resp) end end - rescue Exception - @client_thread.raise($!) + rescue Exception => e + @exception = e + synchronize do + @tagged_response_arrival.broadcast + @continuation_request_arrival.broadcast + end end end + synchronize do + @tagged_response_arrival.broadcast + @continuation_request_arrival.broadcast + end end def get_tagged_response(tag, cmd) until @tagged_responses.key?(tag) + raise @exception if @exception @tagged_response_arrival.wait end resp = @tagged_responses.delete(tag) @@ -1034,6 +1066,9 @@ module Net def send_command(cmd, *args, &block) synchronize do + args.each do |i| + validate_data(i) + end tag = generate_tag put_string(tag + " " + cmd) args.each do |i| @@ -1061,7 +1096,7 @@ module Net @tagno += 1 return format("%s%04d", @tag_prefix, @tagno) end - + def put_string(str) @sock.print(str) if @@debug @@ -1077,6 +1112,25 @@ module Net end end + def validate_data(data) + case data + when nil + when String + when Integer + if data < 0 || data >= 4294967296 + raise DataFormatError, num.to_s + end + when Array + data.each do |i| + validate_data(i) + end + when Time + when Symbol + else + data.validate + end + end + def send_data(data) case data when nil @@ -1110,7 +1164,7 @@ module Net put_string(str) end end - + def send_quoted_string(str) put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"') end @@ -1118,13 +1172,11 @@ module Net def send_literal(str) put_string("{" + str.length.to_s + "}" + CRLF) @continuation_request_arrival.wait + raise @exception if @exception put_string(str) end def send_number_data(num) - if num < 0 || num >= 4294967296 - raise DataFormatError, num.to_s - end put_string(num.to_s) end @@ -1233,11 +1285,56 @@ module Net end end + def create_ssl_params(certs = nil, verify = true) + params = {} + if certs + if File.file?(certs) + params[:ca_file] = certs + elsif File.directory?(certs) + params[:ca_path] = certs + end + end + if verify + params[:verify_mode] = VERIFY_PEER + else + params[:verify_mode] = VERIFY_NONE + end + return params + end + + def start_tls_session(params = {}) + unless defined?(OpenSSL) + raise "SSL extension not installed" + end + if @sock.kind_of?(OpenSSL::SSL::SSLSocket) + raise RuntimeError, "already using SSL" + end + begin + params = params.to_hash + rescue NoMethodError + params = {} + end + context = SSLContext.new + context.set_params(params) + if defined?(VerifyCallbackProc) + context.verify_callback = VerifyCallbackProc + end + @sock = SSLSocket.new(@sock, context) + @sock.sync_close = true + @sock.connect + if context.verify_mode != VERIFY_NONE + @sock.post_connection_check(@host) + end + end + class RawData # :nodoc: def send_data(imap) imap.send(:put_string, @data) end + def validate + end + private def initialize(data) @@ -1250,6 +1347,9 @@ module Net imap.send(:put_string, @data) end + def validate + end + private def initialize(data) @@ -1262,6 +1362,9 @@ module Net imap.send(:send_quoted_string, @data) end + def validate + end + private def initialize(data) @@ -1274,6 +1377,9 @@ module Net imap.send(:send_literal, @data) end + def validate + end + private def initialize(data) @@ -1286,6 +1392,10 @@ module Net imap.send(:put_string, format_internal(@data)) end + def validate + validate_internal(@data) + end + private def initialize(data) @@ -1297,7 +1407,6 @@ module Net when "*" return data when Integer - ensure_nz_number(data) if data == -1 return "*" else @@ -1311,6 +1420,23 @@ module Net when ThreadMember return data.seqno.to_s + ":" + data.children.collect {|i| format_internal(i).join(",")} + end + end + + def validate_internal(data) + case data + when "*" + when Integer + ensure_nz_number(data) + when Range + when Array + data.each do |i| + validate_internal(i) + end + when ThreadMember + data.children.each do |i| + validate_internal(i) + end else raise DataFormatError, data.inspect end @@ -1326,109 +1452,109 @@ module Net end # Net::IMAP::ContinuationRequest represents command continuation requests. - # + # # The command continuation request response is indicated by a "+" token # instead of a tag. This form of response indicates that the server is # ready to accept the continuation of a command from the client. The # remainder of this response is a line of text. - # + # # continue_req ::= "+" SPACE (resp_text / base64) - # + # # ==== Fields: - # + # # data:: Returns the data (Net::IMAP::ResponseText). - # + # # raw_data:: Returns the raw data string. ContinuationRequest = Struct.new(:data, :raw_data) # Net::IMAP::UntaggedResponse represents untagged responses. - # + # # Data transmitted by the server to the client and status responses # that do not indicate command completion are prefixed with the token # "*", and are called untagged responses. - # + # # response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / # mailbox_data / message_data / capability_data) - # + # # ==== Fields: - # + # # name:: Returns the name such as "FLAGS", "LIST", "FETCH".... - # + # # data:: Returns the data such as an array of flag symbols, # a ((<Net::IMAP::MailboxList>)) object.... - # + # # 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 # failure of the operation. It is tagged with the same tag as the # client command which began the operation. - # + # # response_tagged ::= tag SPACE resp_cond_state CRLF - # + # # tag ::= 1*<any ATOM_CHAR except "+"> - # + # # resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text - # + # # ==== Fields: - # + # # tag:: Returns the tag. - # + # # name:: Returns the name. the name is one of "OK", "NO", "BAD". - # + # # data:: Returns the data. See ((<Net::IMAP::ResponseText>)). - # + # # raw_data:: Returns the raw data string. # TaggedResponse = Struct.new(:tag, :name, :data, :raw_data) - + # 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: - # + # # code:: Returns the response code. See ((<Net::IMAP::ResponseCode>)). - # + # # text:: Returns the text. - # + # ResponseText = Struct.new(:code, :text) - # + # # 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: - # + # # name:: Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY".... - # + # # data:: Returns the data if it exists. # ResponseCode = Struct.new(:name, :data) # Net::IMAP::MailboxList represents contents of the LIST response. - # + # # mailbox_list ::= "(" #("\Marked" / "\Noinferiors" / # "\Noselect" / "\Unmarked" / flag_extension) ")" # SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox - # + # # ==== Fields: - # + # # attr:: Returns the name attributes. Each name attribute is a symbol # capitalized by String#capitalize, such as :Noselect (not :NoSelect). - # + # # delim:: Returns the hierarchy delimiter - # + # # name:: Returns the mailbox name. # MailboxList = Struct.new(:attr, :delim, :name) @@ -1437,78 +1563,78 @@ module Net # This object can also be a response to GETQUOTAROOT. In the syntax # specification below, the delimiter used with the "#" construct is a # single space (SPACE). - # + # # quota_list ::= "(" #quota_resource ")" - # + # # quota_resource ::= atom SPACE number SPACE number - # + # # quota_response ::= "QUOTA" SPACE astring SPACE quota_list - # + # # ==== Fields: - # + # # mailbox:: The mailbox with the associated quota. - # + # # usage:: Current storage usage of mailbox. - # + # # quota:: Quota limit imposed on mailbox. # MailboxQuota = Struct.new(:mailbox, :usage, :quota) # Net::IMAP::MailboxQuotaRoot represents part of the GETQUOTAROOT # response. (GETQUOTAROOT can also return Net::IMAP::MailboxQuota.) - # + # # quotaroot_response ::= "QUOTAROOT" SPACE astring *(SPACE astring) - # + # # ==== Fields: - # + # # mailbox:: The mailbox with the associated quota. - # + # # quotaroots:: Zero or more quotaroots that effect the quota on the # specified mailbox. # MailboxQuotaRoot = Struct.new(:mailbox, :quotaroots) # Net::IMAP::MailboxACLItem represents response from GETACL. - # + # # acl_data ::= "ACL" SPACE mailbox *(SPACE identifier SPACE rights) - # + # # identifier ::= astring - # + # # rights ::= astring - # + # # ==== Fields: - # + # # user:: Login name that has certain rights to the mailbox # that was specified with the getacl command. - # + # # rights:: The access rights the indicated user has to the # mailbox. # MailboxACLItem = Struct.new(:user, :rights) # Net::IMAP::StatusData represents contents of the STATUS response. - # + # # ==== Fields: - # + # # mailbox:: Returns the mailbox name. - # + # # attr:: Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT", # "UIDVALIDITY", "UNSEEN". Each value is a number. - # + # StatusData = Struct.new(:mailbox, :attr) # Net::IMAP::FetchData represents contents of the FETCH response. - # + # # ==== Fields: - # + # # seqno:: Returns the message sequence number. # (Note: not the unique identifier, even for the UID command response.) - # + # # attr:: Returns a hash. Each key is a data item name, and each value is # its value. - # + # # The current data items are: - # + # # [BODY] # A form of BODYSTRUCTURE without extension data. # [BODY[<section>]<<origin_octet>>] @@ -1535,67 +1661,67 @@ module Net # Equivalent to BODY[TEXT]. # [UID] # A number expressing the unique identifier of the message. - # + # FetchData = Struct.new(:seqno, :attr) # Net::IMAP::Envelope represents envelope structures of messages. - # + # # ==== Fields: - # + # # date:: Returns a string that represents the date. - # + # # subject:: Returns a string that represents the subject. - # + # # from:: Returns an array of Net::IMAP::Address that represents the from. - # + # # sender:: Returns an array of Net::IMAP::Address that represents the sender. - # + # # reply_to:: Returns an array of Net::IMAP::Address that represents the reply-to. - # + # # to:: Returns an array of Net::IMAP::Address that represents the to. - # + # # cc:: Returns an array of Net::IMAP::Address that represents the cc. - # + # # bcc:: Returns an array of Net::IMAP::Address that represents the bcc. - # + # # in_reply_to:: Returns a string that represents the in-reply-to. - # + # # message_id:: Returns a string that represents the message-id. - # + # Envelope = Struct.new(:date, :subject, :from, :sender, :reply_to, :to, :cc, :bcc, :in_reply_to, :message_id) - # + # # Net::IMAP::Address represents electronic mail addresses. - # + # # ==== Fields: - # + # # name:: Returns the phrase from [RFC-822] mailbox. - # + # # route:: Returns the route from [RFC-822] route-addr. - # + # # mailbox:: nil indicates end of [RFC-822] group. # If non-nil and host is nil, returns [RFC-822] group name. # Otherwise, returns [RFC-822] local-part - # + # # host:: nil indicates [RFC-822] group syntax. # Otherwise, returns [RFC-822] domain name. # Address = Struct.new(:name, :route, :mailbox, :host) - # + # # Net::IMAP::ContentDisposition represents Content-Disposition fields. - # + # # ==== Fields: - # + # # dsp_type:: Returns the disposition type. - # + # # param:: Returns a hash that represents parameters of the Content-Disposition # field. - # + # ContentDisposition = Struct.new(:dsp_type, :param) - # Net::IMAP::ThreadMember represents a thread-node returned + # Net::IMAP::ThreadMember represents a thread-node returned # by Net::IMAP#thread # # ==== Fields: @@ -1608,37 +1734,37 @@ module Net ThreadMember = Struct.new(:seqno, :children) # Net::IMAP::BodyTypeBasic represents basic body structures of messages. - # + # # ==== Fields: - # + # # media_type:: Returns the content media type name as defined in [MIME-IMB]. - # + # # subtype:: Returns the content subtype name as defined in [MIME-IMB]. - # + # # param:: Returns a hash that represents parameters as defined in [MIME-IMB]. - # + # # content_id:: Returns a string giving the content id as defined in [MIME-IMB]. - # + # # description:: Returns a string giving the content description as defined in # [MIME-IMB]. - # + # # encoding:: Returns a string giving the content transfer encoding as defined in # [MIME-IMB]. - # + # # size:: Returns a number giving the size of the body in octets. - # + # # md5:: Returns a string giving the body MD5 value as defined in [MD5]. - # + # # disposition:: Returns a Net::IMAP::ContentDisposition object giving # the content disposition. - # + # # language:: Returns a string or an array of strings giving the body # language value as defined in [LANGUAGE-TAGS]. - # + # # extension:: Returns extension data. - # + # # multipart?:: Returns false. - # + # class BodyTypeBasic < Struct.new(:media_type, :subtype, :param, :content_id, :description, :encoding, :size, @@ -1649,7 +1775,7 @@ module Net end # Obsolete: use +subtype+ instead. Calling this will - # generate a warning message to +stderr+, then return + # generate a warning message to +stderr+, then return # the value of +subtype+. def media_subtype $stderr.printf("warning: media_subtype is obsolete.\n") @@ -1659,13 +1785,13 @@ module Net end # Net::IMAP::BodyTypeText represents TEXT body structures of messages. - # + # # ==== Fields: - # + # # lines:: Returns the size of the body in text lines. - # + # # And Net::IMAP::BodyTypeText has all fields of Net::IMAP::BodyTypeBasic. - # + # class BodyTypeText < Struct.new(:media_type, :subtype, :param, :content_id, :description, :encoding, :size, @@ -1677,7 +1803,7 @@ module Net end # Obsolete: use +subtype+ instead. Calling this will - # generate a warning message to +stderr+, then return + # generate a warning message to +stderr+, then return # the value of +subtype+. def media_subtype $stderr.printf("warning: media_subtype is obsolete.\n") @@ -1687,13 +1813,13 @@ module Net end # Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages. - # + # # ==== Fields: - # + # # envelope:: Returns a Net::IMAP::Envelope giving the envelope structure. - # + # # body:: Returns an object giving the body structure. - # + # # And Net::IMAP::BodyTypeMessage has all methods of Net::IMAP::BodyTypeText. # class BodyTypeMessage < Struct.new(:media_type, :subtype, @@ -1707,7 +1833,7 @@ module Net end # Obsolete: use +subtype+ instead. Calling this will - # generate a warning message to +stderr+, then return + # generate a warning message to +stderr+, then return # the value of +subtype+. def media_subtype $stderr.printf("warning: media_subtype is obsolete.\n") @@ -1716,29 +1842,29 @@ module Net end end - # Net::IMAP::BodyTypeMultipart represents multipart body structures + # Net::IMAP::BodyTypeMultipart represents multipart body structures # of messages. - # + # # ==== Fields: - # + # # media_type:: Returns the content media type name as defined in [MIME-IMB]. - # + # # subtype:: Returns the content subtype name as defined in [MIME-IMB]. - # + # # parts:: Returns multiple parts. - # + # # param:: Returns a hash that represents parameters as defined in [MIME-IMB]. - # + # # disposition:: Returns a Net::IMAP::ContentDisposition object giving # the content disposition. - # + # # language:: Returns a string or an array of strings giving the body # language value as defined in [LANGUAGE-TAGS]. - # + # # extension:: Returns extension data. - # + # # multipart?:: Returns true. - # + # class BodyTypeMultipart < Struct.new(:media_type, :subtype, :parts, :param, :disposition, :language, @@ -1748,7 +1874,7 @@ module Net end # Obsolete: use +subtype+ instead. Calling this will - # generate a warning message to +stderr+, then return + # generate a warning message to +stderr+, then return # the value of +subtype+. def media_subtype $stderr.printf("warning: media_subtype is obsolete.\n") @@ -2549,35 +2675,35 @@ module Net def thread_branch(token) rootmember = nil lastmember = nil - + while true shift_token # ignore first T_LPAR token = lookahead - + case token.symbol when T_NUMBER # new member newmember = ThreadMember.new(number, []) if rootmember.nil? rootmember = newmember - else + else lastmember.children << newmember - end + end lastmember = newmember - when T_SPACE - # do nothing + when T_SPACE + # do nothing when T_LPAR if rootmember.nil? # dummy member lastmember = rootmember = ThreadMember.new(nil, []) - end - + end + lastmember.children << thread_branch(token) when T_RPAR - break - end + break + end end - + return rootmember end @@ -2997,7 +3123,7 @@ module Net parse_error("unknown token - %s", $&.dump) end else - parse_error("illegal @lex_state - %s", @lex_state.inspect) + parse_error("invalid @lex_state - %s", @lex_state.inspect) end end @@ -3054,7 +3180,7 @@ module Net @user = user @password = password end - end + end add_authenticator "PLAIN", PlainAuthenticator # Authenticator for the "CRAM-MD5" authentication type. See @@ -3080,8 +3206,8 @@ module Net k_ipad = key + "\0" * (64 - key.length) k_opad = key + "\0" * (64 - key.length) for i in 0..63 - k_ipad[i] ^= 0x36 - k_opad[i] ^= 0x5c + k_ipad[i] = (k_ipad[i].ord ^ 0x36).chr + k_opad[i] = (k_opad[i].ord ^ 0x5c).chr end digest = Digest::MD5.digest(k_ipad + text) @@ -3145,7 +3271,7 @@ module Net ].join(':') ) - return response.keys.map { |k| qdval(k.to_s, response[k]) }.join(',') + return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',') when STAGE_TWO @stage = nil # if at the second stage, return an empty string @@ -3178,7 +3304,7 @@ module Net return @nc[nonce] end - # some reponses needs quoting + # some responses need quoting def qdval(k, v) return if k.nil? or v.nil? if %w"username authzid realm nonce cnonce digest-uri qop".include? k @@ -3219,7 +3345,7 @@ module Net class BadResponseError < ResponseError end - # Error raised upon a "BYE" response from the server, indicating + # Error raised upon a "BYE" response from the server, indicating # that the client is not being allowed to login, or has been timed # out due to inactivity. class ByeResponseError < ResponseError @@ -3304,9 +3430,8 @@ EOF usage exit(1) end - $port ||= $ssl ? 993 : 143 - - imap = Net::IMAP.new($host, $port, $ssl) + + imap = Net::IMAP.new($host, :port => $port, :ssl => $ssl) begin password = get_password imap.authenticate($auth, $user, password) |