зеркало из https://github.com/github/ruby.git
Promote net-imap to the bundled gems
This commit is contained in:
Родитель
e49c998d1e
Коммит
d5bc6b2337
|
@ -158,10 +158,6 @@ Yukihiro Matsumoto (matz)
|
|||
NARUSE, Yui (naruse)
|
||||
https://github.com/ruby/net-http
|
||||
https://rubygems.org/gems/net-http
|
||||
[lib/net/imap.rb]
|
||||
Shugo Maeda (shugo)
|
||||
https://github.com/ruby/net-imap
|
||||
https://rubygems.org/gems/net-imap
|
||||
[lib/net/pop.rb]
|
||||
_unmaintained_
|
||||
https://github.com/ruby/net-pop
|
||||
|
@ -389,6 +385,8 @@ Yukihiro Matsumoto (matz)
|
|||
https://github.com/ruby/rss
|
||||
[net-ftp]
|
||||
https://github.com/ruby/net-ftp
|
||||
[net-imap]
|
||||
https://github.com/ruby/net-imap
|
||||
[matrix]
|
||||
https://github.com/ruby/matrix
|
||||
[prime]
|
||||
|
|
|
@ -47,7 +47,6 @@ OptionParser:: Ruby-oriented class for command-line option analysis
|
|||
Logger:: Provides a simple logging utility for outputting messages
|
||||
Mutex_m:: Mixin to extend objects to be handled like a Mutex
|
||||
Net::HTTP:: HTTP client api for Ruby
|
||||
Net::IMAP:: Ruby client api for Internet Message Access Protocol
|
||||
Net::POP3:: Ruby client library for POP3
|
||||
Net::SMTP:: Simple Mail Transfer Protocol client library for Ruby
|
||||
Observable:: Provides a mechanism for publish/subscribe pattern in Ruby
|
||||
|
@ -109,6 +108,7 @@ Test::Unit:: A compatibility layer for MiniTest
|
|||
REXML:: An XML toolkit for Ruby
|
||||
RSS:: Family of libraries that support various formats of XML "feeds"
|
||||
Net::FTP:: Support for the File Transfer Protocol
|
||||
Net::IMAP:: Ruby client api for Internet Message Access Protocol
|
||||
Matrix:: Represents a mathematical matrix.
|
||||
Prime:: Prime numbers and factorization library
|
||||
RBS:: RBS is a language to describe the structure of Ruby programs
|
||||
|
|
|
@ -6,6 +6,7 @@ test-unit 3.4.1 https://github.com/test-unit/test-unit 3.4.1
|
|||
rexml 3.2.5 https://github.com/ruby/rexml
|
||||
rss 0.2.9 https://github.com/ruby/rss 0.2.9
|
||||
net-ftp 0.1.2 https://github.com/ruby/net-ftp
|
||||
net-imap 0.2.1 https://github.com/ruby/net-imap
|
||||
matrix 0.4.1 https://github.com/ruby/matrix
|
||||
prime 0.1.2 https://github.com/ruby/prime
|
||||
typeprof 0.14.1 https://github.com/ruby/typeprof
|
||||
|
|
1573
lib/net/imap.rb
1573
lib/net/imap.rb
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,44 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Registry for SASL authenticators used by Net::IMAP.
|
||||
module Net::IMAP::Authenticators
|
||||
|
||||
# Adds an authenticator for use with Net::IMAP#authenticate. +auth_type+ is the
|
||||
# {SASL mechanism}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
||||
# supported by +authenticator+ (for instance, "+PLAIN+"). The +authenticator+
|
||||
# is an object which defines a +#process+ method to handle authentication with
|
||||
# the server. See Net::IMAP::PlainAuthenticator, 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.
|
||||
def add_authenticator(auth_type, authenticator)
|
||||
authenticators[auth_type] = authenticator
|
||||
end
|
||||
|
||||
# Builds an authenticator for Net::IMAP#authenticate. +args+ will be passed
|
||||
# directly to the chosen authenticator's +#initialize+.
|
||||
def authenticator(auth_type, *args)
|
||||
auth_type = auth_type.upcase
|
||||
unless authenticators.has_key?(auth_type)
|
||||
raise ArgumentError,
|
||||
format('unknown auth type - "%s"', auth_type)
|
||||
end
|
||||
authenticators[auth_type].new(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authenticators
|
||||
@authenticators ||= {}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Net::IMAP.extend Net::IMAP::Authenticators
|
||||
|
||||
require_relative "authenticators/login"
|
||||
require_relative "authenticators/plain"
|
||||
require_relative "authenticators/cram_md5"
|
||||
require_relative "authenticators/digest_md5"
|
|
@ -1,49 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "digest/md5"
|
||||
|
||||
# Authenticator for the "+CRAM-MD5+" SASL mechanism, specified in
|
||||
# RFC2195[https://tools.ietf.org/html/rfc2195]. See Net::IMAP#authenticate.
|
||||
#
|
||||
# == Deprecated
|
||||
#
|
||||
# +CRAM-MD5+ is obsolete and insecure. It is included for compatibility with
|
||||
# existing servers.
|
||||
# {draft-ietf-sasl-crammd5-to-historic}[https://tools.ietf.org/html/draft-ietf-sasl-crammd5-to-historic-00.html]
|
||||
# recommends using +SCRAM-*+ or +PLAIN+ protected by TLS instead.
|
||||
#
|
||||
# Additionally, RFC8314[https://tools.ietf.org/html/rfc8314] discourage the use
|
||||
# of cleartext and recommends TLS version 1.2 or greater be used for all
|
||||
# traffic. With TLS +CRAM-MD5+ is okay, but so is +PLAIN+
|
||||
class Net::IMAP::CramMD5Authenticator
|
||||
def process(challenge)
|
||||
digest = hmac_md5(challenge, @password)
|
||||
return @user + " " + digest
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(user, password)
|
||||
@user = user
|
||||
@password = password
|
||||
end
|
||||
|
||||
def hmac_md5(text, key)
|
||||
if key.length > 64
|
||||
key = Digest::MD5.digest(key)
|
||||
end
|
||||
|
||||
k_ipad = key + "\0" * (64 - key.length)
|
||||
k_opad = key + "\0" * (64 - key.length)
|
||||
for i in 0..63
|
||||
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)
|
||||
|
||||
return Digest::MD5.hexdigest(k_opad + digest)
|
||||
end
|
||||
|
||||
Net::IMAP.add_authenticator "PLAIN", self
|
||||
end
|
|
@ -1,111 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "digest/md5"
|
||||
require "strscan"
|
||||
|
||||
# Net::IMAP authenticator for the "`DIGEST-MD5`" SASL mechanism type, specified
|
||||
# in RFC2831(https://tools.ietf.org/html/rfc2831). See Net::IMAP#authenticate.
|
||||
#
|
||||
# == Deprecated
|
||||
#
|
||||
# "+DIGEST-MD5+" has been deprecated by
|
||||
# {RFC6331}[https://tools.ietf.org/html/rfc6331] and should not be relied on for
|
||||
# security. It is included for compatibility with existing servers.
|
||||
class Net::IMAP::DigestMD5Authenticator
|
||||
def process(challenge)
|
||||
case @stage
|
||||
when STAGE_ONE
|
||||
@stage = STAGE_TWO
|
||||
sparams = {}
|
||||
c = StringScanner.new(challenge)
|
||||
while c.scan(/(?:\s*,)?\s*(\w+)=("(?:[^\\"]+|\\.)*"|[^,]+)\s*/)
|
||||
k, v = c[1], c[2]
|
||||
if v =~ /^"(.*)"$/
|
||||
v = $1
|
||||
if v =~ /,/
|
||||
v = v.split(',')
|
||||
end
|
||||
end
|
||||
sparams[k] = v
|
||||
end
|
||||
|
||||
raise DataFormatError, "Bad Challenge: '#{challenge}'" unless c.rest.size == 0
|
||||
raise Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth")
|
||||
|
||||
response = {
|
||||
:nonce => sparams['nonce'],
|
||||
:username => @user,
|
||||
:realm => sparams['realm'],
|
||||
:cnonce => Digest::MD5.hexdigest("%.15f:%.15f:%d" % [Time.now.to_f, rand, Process.pid.to_s]),
|
||||
:'digest-uri' => 'imap/' + sparams['realm'],
|
||||
:qop => 'auth',
|
||||
:maxbuf => 65535,
|
||||
:nc => "%08d" % nc(sparams['nonce']),
|
||||
:charset => sparams['charset'],
|
||||
}
|
||||
|
||||
response[:authzid] = @authname unless @authname.nil?
|
||||
|
||||
# now, the real thing
|
||||
a0 = Digest::MD5.digest( [ response.values_at(:username, :realm), @password ].join(':') )
|
||||
|
||||
a1 = [ a0, response.values_at(:nonce,:cnonce) ].join(':')
|
||||
a1 << ':' + response[:authzid] unless response[:authzid].nil?
|
||||
|
||||
a2 = "AUTHENTICATE:" + response[:'digest-uri']
|
||||
a2 << ":00000000000000000000000000000000" if response[:qop] and response[:qop] =~ /^auth-(?:conf|int)$/
|
||||
|
||||
response[:response] = Digest::MD5.hexdigest(
|
||||
[
|
||||
Digest::MD5.hexdigest(a1),
|
||||
response.values_at(:nonce, :nc, :cnonce, :qop),
|
||||
Digest::MD5.hexdigest(a2)
|
||||
].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
|
||||
if challenge =~ /rspauth=/
|
||||
return ''
|
||||
else
|
||||
raise ResponseParseError, challenge
|
||||
end
|
||||
else
|
||||
raise ResponseParseError, challenge
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(user, password, authname = nil)
|
||||
@user, @password, @authname = user, password, authname
|
||||
@nc, @stage = {}, STAGE_ONE
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
STAGE_ONE = :stage_one
|
||||
STAGE_TWO = :stage_two
|
||||
|
||||
def nc(nonce)
|
||||
if @nc.has_key? nonce
|
||||
@nc[nonce] = @nc[nonce] + 1
|
||||
else
|
||||
@nc[nonce] = 1
|
||||
end
|
||||
return @nc[nonce]
|
||||
end
|
||||
|
||||
# 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
|
||||
v.gsub!(/([\\"])/, "\\\1")
|
||||
return '%s="%s"' % [k, v]
|
||||
else
|
||||
return '%s=%s' % [k, v]
|
||||
end
|
||||
end
|
||||
|
||||
Net::IMAP.add_authenticator "DIGEST-MD5", self
|
||||
end
|
|
@ -1,43 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Authenticator for the "+LOGIN+" SASL mechanism. See Net::IMAP#authenticate.
|
||||
#
|
||||
# +LOGIN+ authentication sends the password in cleartext.
|
||||
# RFC3501[https://tools.ietf.org/html/rfc3501] encourages servers to disable
|
||||
# cleartext authentication until after TLS has been negotiated.
|
||||
# RFC8314[https://tools.ietf.org/html/rfc8314] recommends TLS version 1.2 or
|
||||
# greater be used for all traffic, and deprecate cleartext access ASAP. +LOGIN+
|
||||
# can be secured by TLS encryption.
|
||||
#
|
||||
# == Deprecated
|
||||
#
|
||||
# The {SASL mechanisms
|
||||
# registry}[https://www.iana.org/assignments/sasl-mechanisms/sasl-mechanisms.xhtml]
|
||||
# marks "LOGIN" as obsoleted in favor of "PLAIN". It is included here for
|
||||
# compatibility with existing servers. See
|
||||
# {draft-murchison-sasl-login}[https://www.iana.org/go/draft-murchison-sasl-login]
|
||||
# for both specification and deprecation.
|
||||
class Net::IMAP::LoginAuthenticator
|
||||
def process(data)
|
||||
case @state
|
||||
when STATE_USER
|
||||
@state = STATE_PASSWORD
|
||||
return @user
|
||||
when STATE_PASSWORD
|
||||
return @password
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
STATE_USER = :USER
|
||||
STATE_PASSWORD = :PASSWORD
|
||||
|
||||
def initialize(user, password)
|
||||
@user = user
|
||||
@password = password
|
||||
@state = STATE_USER
|
||||
end
|
||||
|
||||
Net::IMAP.add_authenticator "LOGIN", self
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Authenticator for the "+PLAIN+" SASL mechanism, specified in
|
||||
# RFC4616[https://tools.ietf.org/html/rfc4616]. See Net::IMAP#authenticate.
|
||||
#
|
||||
# +PLAIN+ authentication sends the password in cleartext.
|
||||
# RFC3501[https://tools.ietf.org/html/rfc3501] encourages servers to disable
|
||||
# cleartext authentication until after TLS has been negotiated.
|
||||
# RFC8314[https://tools.ietf.org/html/rfc8314] recommends TLS version 1.2 or
|
||||
# greater be used for all traffic, and deprecate cleartext access ASAP. +PLAIN+
|
||||
# can be secured by TLS encryption.
|
||||
class Net::IMAP::PlainAuthenticator
|
||||
|
||||
def process(data)
|
||||
return "#@authzid\0#@username\0#@password"
|
||||
end
|
||||
|
||||
# :nodoc:
|
||||
NULL = -"\0".b
|
||||
|
||||
private
|
||||
|
||||
# +username+ is the authentication identity, the identity whose +password+ is
|
||||
# used. +username+ is referred to as +authcid+ by
|
||||
# RFC4616[https://tools.ietf.org/html/rfc4616].
|
||||
#
|
||||
# +authzid+ is the authorization identity (identity to act as). It can
|
||||
# usually be left blank. When +authzid+ is left blank (nil or empty string)
|
||||
# the server will derive an identity from the credentials and use that as the
|
||||
# authorization identity.
|
||||
def initialize(username, password, authzid: nil)
|
||||
raise ArgumentError, "username contains NULL" if username&.include?(NULL)
|
||||
raise ArgumentError, "password contains NULL" if password&.include?(NULL)
|
||||
raise ArgumentError, "authzid contains NULL" if authzid&.include?(NULL)
|
||||
@username = username
|
||||
@password = password
|
||||
@authzid = authzid
|
||||
end
|
||||
|
||||
Net::IMAP.add_authenticator "PLAIN", self
|
||||
end
|
|
@ -1,301 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Net
|
||||
class IMAP < Protocol
|
||||
|
||||
private
|
||||
|
||||
def validate_data(data)
|
||||
case data
|
||||
when nil
|
||||
when String
|
||||
when Integer
|
||||
NumValidator.ensure_number(data)
|
||||
when Array
|
||||
if data[0] == 'CHANGEDSINCE'
|
||||
NumValidator.ensure_mod_sequence_value(data[1])
|
||||
else
|
||||
data.each do |i|
|
||||
validate_data(i)
|
||||
end
|
||||
end
|
||||
when Time
|
||||
when Symbol
|
||||
else
|
||||
data.validate
|
||||
end
|
||||
end
|
||||
|
||||
def send_data(data, tag = nil)
|
||||
case data
|
||||
when nil
|
||||
put_string("NIL")
|
||||
when String
|
||||
send_string_data(data, tag)
|
||||
when Integer
|
||||
send_number_data(data)
|
||||
when Array
|
||||
send_list_data(data, tag)
|
||||
when Time
|
||||
send_time_data(data)
|
||||
when Symbol
|
||||
send_symbol_data(data)
|
||||
else
|
||||
data.send_data(self, tag)
|
||||
end
|
||||
end
|
||||
|
||||
def send_string_data(str, tag = nil)
|
||||
case str
|
||||
when ""
|
||||
put_string('""')
|
||||
when /[\x80-\xff\r\n]/n
|
||||
# literal
|
||||
send_literal(str, tag)
|
||||
when /[(){ \x00-\x1f\x7f%*"\\]/n
|
||||
# quoted string
|
||||
send_quoted_string(str)
|
||||
else
|
||||
put_string(str)
|
||||
end
|
||||
end
|
||||
|
||||
def send_quoted_string(str)
|
||||
put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
|
||||
end
|
||||
|
||||
def send_literal(str, tag = nil)
|
||||
synchronize do
|
||||
put_string("{" + str.bytesize.to_s + "}" + CRLF)
|
||||
@continued_command_tag = tag
|
||||
@continuation_request_exception = nil
|
||||
begin
|
||||
@continuation_request_arrival.wait
|
||||
e = @continuation_request_exception || @exception
|
||||
raise e if e
|
||||
put_string(str)
|
||||
ensure
|
||||
@continued_command_tag = nil
|
||||
@continuation_request_exception = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def send_number_data(num)
|
||||
put_string(num.to_s)
|
||||
end
|
||||
|
||||
def send_list_data(list, tag = nil)
|
||||
put_string("(")
|
||||
first = true
|
||||
list.each do |i|
|
||||
if first
|
||||
first = false
|
||||
else
|
||||
put_string(" ")
|
||||
end
|
||||
send_data(i, tag)
|
||||
end
|
||||
put_string(")")
|
||||
end
|
||||
|
||||
DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
|
||||
|
||||
def send_time_data(time)
|
||||
t = time.dup.gmtime
|
||||
s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"',
|
||||
t.day, DATE_MONTH[t.month - 1], t.year,
|
||||
t.hour, t.min, t.sec)
|
||||
put_string(s)
|
||||
end
|
||||
|
||||
def send_symbol_data(symbol)
|
||||
put_string("\\" + symbol.to_s)
|
||||
end
|
||||
|
||||
class RawData # :nodoc:
|
||||
def send_data(imap, tag)
|
||||
imap.__send__(:put_string, @data)
|
||||
end
|
||||
|
||||
def validate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
|
||||
class Atom # :nodoc:
|
||||
def send_data(imap, tag)
|
||||
imap.__send__(:put_string, @data)
|
||||
end
|
||||
|
||||
def validate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
|
||||
class QuotedString # :nodoc:
|
||||
def send_data(imap, tag)
|
||||
imap.__send__(:send_quoted_string, @data)
|
||||
end
|
||||
|
||||
def validate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
|
||||
class Literal # :nodoc:
|
||||
def send_data(imap, tag)
|
||||
imap.__send__(:send_literal, @data, tag)
|
||||
end
|
||||
|
||||
def validate
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
end
|
||||
|
||||
class MessageSet # :nodoc:
|
||||
def send_data(imap, tag)
|
||||
imap.__send__(:put_string, format_internal(@data))
|
||||
end
|
||||
|
||||
def validate
|
||||
validate_internal(@data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
|
||||
def format_internal(data)
|
||||
case data
|
||||
when "*"
|
||||
return data
|
||||
when Integer
|
||||
if data == -1
|
||||
return "*"
|
||||
else
|
||||
return data.to_s
|
||||
end
|
||||
when Range
|
||||
return format_internal(data.first) +
|
||||
":" + format_internal(data.last)
|
||||
when Array
|
||||
return data.collect {|i| format_internal(i)}.join(",")
|
||||
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
|
||||
NumValidator.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
|
||||
end
|
||||
end
|
||||
|
||||
class ClientID # :nodoc:
|
||||
|
||||
def send_data(imap, tag)
|
||||
imap.__send__(:send_data, format_internal(@data), tag)
|
||||
end
|
||||
|
||||
def validate
|
||||
validate_internal(@data)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
|
||||
def validate_internal(client_id)
|
||||
client_id.to_h.each do |k,v|
|
||||
unless StringFormatter.valid_string?(k)
|
||||
raise DataFormatError, client_id.inspect
|
||||
end
|
||||
end
|
||||
rescue NoMethodError, TypeError # to_h failed
|
||||
raise DataFormatError, client_id.inspect
|
||||
end
|
||||
|
||||
def format_internal(client_id)
|
||||
return nil if client_id.nil?
|
||||
client_id.to_h.flat_map {|k,v|
|
||||
[StringFormatter.string(k), StringFormatter.nstring(v)]
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module StringFormatter
|
||||
|
||||
LITERAL_REGEX = /[\x80-\xff\r\n]/n
|
||||
|
||||
module_function
|
||||
|
||||
# Allows symbols in addition to strings
|
||||
def valid_string?(str)
|
||||
str.is_a?(Symbol) || str.respond_to?(:to_str)
|
||||
end
|
||||
|
||||
# Allows nil, symbols, and strings
|
||||
def valid_nstring?(str)
|
||||
str.nil? || valid_string?(str)
|
||||
end
|
||||
|
||||
# coerces using +to_s+
|
||||
def string(str)
|
||||
str = str.to_s
|
||||
if str =~ LITERAL_REGEX
|
||||
Literal.new(str)
|
||||
else
|
||||
QuotedString.new(str)
|
||||
end
|
||||
end
|
||||
|
||||
# coerces non-nil using +to_s+
|
||||
def nstring(str)
|
||||
str.nil? ? nil : string(str)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Net
|
||||
class IMAP < Protocol
|
||||
|
||||
# Decode a string from modified UTF-7 format to UTF-8.
|
||||
#
|
||||
# UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a
|
||||
# slightly modified version of this to encode mailbox names
|
||||
# containing non-ASCII characters; see [IMAP] section 5.1.3.
|
||||
#
|
||||
# Net::IMAP does _not_ automatically encode and decode
|
||||
# mailbox names to and from UTF-7.
|
||||
def self.decode_utf7(s)
|
||||
return s.gsub(/&([^-]+)?-/n) {
|
||||
if $1
|
||||
($1.tr(",", "/") + "===").unpack1("m").encode(Encoding::UTF_8, Encoding::UTF_16BE)
|
||||
else
|
||||
"&"
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# Encode a string from UTF-8 format to modified UTF-7.
|
||||
def self.encode_utf7(s)
|
||||
return s.gsub(/(&)|[^\x20-\x7e]+/) {
|
||||
if $1
|
||||
"&-"
|
||||
else
|
||||
base64 = [$&.encode(Encoding::UTF_16BE)].pack("m0")
|
||||
"&" + base64.delete("=").tr("/", ",") + "-"
|
||||
end
|
||||
}.force_encoding("ASCII-8BIT")
|
||||
end
|
||||
|
||||
# Formats +time+ as an IMAP-style date.
|
||||
def self.format_date(time)
|
||||
return time.strftime('%d-%b-%Y')
|
||||
end
|
||||
|
||||
# Formats +time+ as an IMAP-style date-time.
|
||||
def self.format_datetime(time)
|
||||
return time.strftime('%d-%b-%Y %H:%M %z')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,76 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Net
|
||||
class IMAP < Protocol
|
||||
|
||||
# :category: Message Flags
|
||||
#
|
||||
# Flag indicating a message has been seen.
|
||||
SEEN = :Seen
|
||||
|
||||
# :category: Message Flags
|
||||
#
|
||||
# Flag indicating a message has been answered.
|
||||
ANSWERED = :Answered
|
||||
|
||||
# :category: Message Flags
|
||||
#
|
||||
# Flag indicating a message has been flagged for special or urgent
|
||||
# attention.
|
||||
FLAGGED = :Flagged
|
||||
|
||||
# :category: Message Flags
|
||||
#
|
||||
# Flag indicating a message has been marked for deletion. This
|
||||
# will occur when the mailbox is closed or expunged.
|
||||
DELETED = :Deleted
|
||||
|
||||
# :category: Message Flags
|
||||
#
|
||||
# Flag indicating a message is only a draft or work-in-progress version.
|
||||
DRAFT = :Draft
|
||||
|
||||
# :category: Message Flags
|
||||
#
|
||||
# Flag indicating that the message is "recent," meaning that this
|
||||
# session is the first session in which the client has been notified
|
||||
# of this message.
|
||||
RECENT = :Recent
|
||||
|
||||
# :category: Mailbox Flags
|
||||
#
|
||||
# Flag indicating that a mailbox context name cannot contain
|
||||
# children.
|
||||
NOINFERIORS = :Noinferiors
|
||||
|
||||
# :category: Mailbox Flags
|
||||
#
|
||||
# Flag indicating that a mailbox is not selected.
|
||||
NOSELECT = :Noselect
|
||||
|
||||
# :category: Mailbox Flags
|
||||
#
|
||||
# Flag indicating that a mailbox has been marked "interesting" by
|
||||
# the server; this commonly indicates that the mailbox contains
|
||||
# new messages.
|
||||
MARKED = :Marked
|
||||
|
||||
# :category: Mailbox Flags
|
||||
#
|
||||
# Flag indicating that the mailbox does not contains new messages.
|
||||
UNMARKED = :Unmarked
|
||||
|
||||
@@max_flag_count = 10000
|
||||
|
||||
# Returns the max number of flags interned to symbols.
|
||||
def self.max_flag_count
|
||||
return @@max_flag_count
|
||||
end
|
||||
|
||||
# Sets the max number of flags interned to symbols.
|
||||
def self.max_flag_count=(count)
|
||||
@@max_flag_count = count
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
name = File.basename(__FILE__, ".gemspec")
|
||||
version = ["lib", Array.new(name.count("-")+1, "..").join("/")].find do |dir|
|
||||
break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
|
||||
/^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
|
||||
end rescue nil
|
||||
end
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = name
|
||||
spec.version = version
|
||||
spec.authors = ["Shugo Maeda"]
|
||||
spec.email = ["shugo@ruby-lang.org"]
|
||||
|
||||
spec.summary = %q{Ruby client api for Internet Message Access Protocol}
|
||||
spec.description = %q{Ruby client api for Internet Message Access Protocol}
|
||||
spec.homepage = "https://github.com/ruby/net-imap"
|
||||
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
||||
spec.licenses = ["Ruby", "BSD-2-Clause"]
|
||||
|
||||
spec.metadata["homepage_uri"] = spec.homepage
|
||||
spec.metadata["source_code_uri"] = spec.homepage
|
||||
|
||||
# Specify which files should be added to the gem when it is released.
|
||||
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
||||
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
||||
`git ls-files -z 2>/dev/null`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
||||
end
|
||||
spec.bindir = "exe"
|
||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||
spec.require_paths = ["lib"]
|
||||
|
||||
spec.add_dependency "net-protocol"
|
||||
spec.add_dependency "digest"
|
||||
spec.add_dependency "strscan"
|
||||
end
|
|
@ -1,527 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Net
|
||||
class IMAP < Protocol
|
||||
|
||||
# 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.
|
||||
class ContinuationRequest < Struct.new(:data, :raw_data)
|
||||
end
|
||||
|
||||
# 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", or "FETCH".
|
||||
#
|
||||
# data:: Returns the data such as an array of flag symbols,
|
||||
# a ((<Net::IMAP::MailboxList>)) object.
|
||||
#
|
||||
# raw_data:: Returns the raw data string.
|
||||
class UntaggedResponse < Struct.new(:name, :data, :raw_data)
|
||||
end
|
||||
|
||||
# Net::IMAP::IgnoredResponse represents intentionally ignored responses.
|
||||
#
|
||||
# This includes untagged response "NOOP" sent by eg. Zimbra to avoid some
|
||||
# clients to close the connection.
|
||||
#
|
||||
# It matches no IMAP standard.
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# raw_data:: Returns the raw data string.
|
||||
class IgnoredResponse < Struct.new(:raw_data)
|
||||
end
|
||||
|
||||
# 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, one of "OK", "NO", or "BAD".
|
||||
#
|
||||
# data:: Returns the data. See ((<Net::IMAP::ResponseText>)).
|
||||
#
|
||||
# raw_data:: Returns the raw data string.
|
||||
#
|
||||
class TaggedResponse < Struct.new(:tag, :name, :data, :raw_data)
|
||||
end
|
||||
|
||||
# Net::IMAP::ResponseText represents texts of responses.
|
||||
# The text may be prefixed by the response code.
|
||||
#
|
||||
# resp_text ::= ["[" resp-text-code "]" SP] text
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# code:: Returns the response code. See ((<Net::IMAP::ResponseCode>)).
|
||||
#
|
||||
# text:: Returns the text.
|
||||
#
|
||||
class ResponseText < Struct.new(:code, :text)
|
||||
end
|
||||
|
||||
# Net::IMAP::ResponseCode represents response codes.
|
||||
#
|
||||
# resp_text_code ::= "ALERT" /
|
||||
# "BADCHARSET" [SP "(" astring *(SP astring) ")" ] /
|
||||
# capability_data / "PARSE" /
|
||||
# "PERMANENTFLAGS" SP "("
|
||||
# [flag_perm *(SP flag_perm)] ")" /
|
||||
# "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
|
||||
# "UIDNEXT" SP nz_number / "UIDVALIDITY" SP nz_number /
|
||||
# "UNSEEN" SP nz_number /
|
||||
# atom [SP 1*<any TEXT-CHAR except "]">]
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# name:: Returns the name, such as "ALERT", "PERMANENTFLAGS", or "UIDVALIDITY".
|
||||
#
|
||||
# data:: Returns the data, if it exists.
|
||||
#
|
||||
class ResponseCode < Struct.new(:name, :data)
|
||||
end
|
||||
|
||||
# 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.
|
||||
#
|
||||
class MailboxList < Struct.new(:attr, :delim, :name)
|
||||
end
|
||||
|
||||
# Net::IMAP::MailboxQuota represents contents of GETQUOTA response.
|
||||
# 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 the mailbox.
|
||||
#
|
||||
# quota:: Quota limit imposed on the mailbox.
|
||||
#
|
||||
class MailboxQuota < Struct.new(:mailbox, :usage, :quota)
|
||||
end
|
||||
|
||||
# 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 affect the quota on the
|
||||
# specified mailbox.
|
||||
#
|
||||
class MailboxQuotaRoot < Struct.new(:mailbox, :quotaroots)
|
||||
end
|
||||
|
||||
# Net::IMAP::MailboxACLItem represents the 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.
|
||||
#
|
||||
class MailboxACLItem < Struct.new(:user, :rights, :mailbox)
|
||||
end
|
||||
|
||||
# Net::IMAP::Namespace represents a single [RFC-2342] namespace.
|
||||
#
|
||||
# Namespace = nil / "(" 1*( "(" string SP (<"> QUOTED_CHAR <"> /
|
||||
# nil) *(Namespace_Response_Extension) ")" ) ")"
|
||||
#
|
||||
# Namespace_Response_Extension = SP string SP "(" string *(SP string)
|
||||
# ")"
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# prefix:: Returns the namespace prefix string.
|
||||
# delim:: Returns nil or the hierarchy delimiter character.
|
||||
# extensions:: Returns a hash of extension names to extension flag arrays.
|
||||
#
|
||||
class Namespace < Struct.new(:prefix, :delim, :extensions)
|
||||
end
|
||||
|
||||
# Net::IMAP::Namespaces represents the response from [RFC-2342] NAMESPACE.
|
||||
#
|
||||
# Namespace_Response = "*" SP "NAMESPACE" SP Namespace SP Namespace SP
|
||||
# Namespace
|
||||
#
|
||||
# ; The first Namespace is the Personal Namespace(s)
|
||||
# ; The second Namespace is the Other Users' Namespace(s)
|
||||
# ; The third Namespace is the Shared Namespace(s)
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# personal:: Returns an array of Personal Net::IMAP::Namespace objects.
|
||||
# other:: Returns an array of Other Users' Net::IMAP::Namespace objects.
|
||||
# shared:: Returns an array of Shared Net::IMAP::Namespace objects.
|
||||
#
|
||||
class Namespaces < Struct.new(:personal, :other, :shared)
|
||||
end
|
||||
|
||||
# Net::IMAP::StatusData represents the 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.
|
||||
#
|
||||
class StatusData < Struct.new(:mailbox, :attr)
|
||||
end
|
||||
|
||||
# Net::IMAP::FetchData represents the 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>>]
|
||||
# A string expressing the body contents of the specified section.
|
||||
# [BODYSTRUCTURE]
|
||||
# An object that describes the [MIME-IMB] body structure of a message.
|
||||
# See Net::IMAP::BodyTypeBasic, Net::IMAP::BodyTypeText,
|
||||
# Net::IMAP::BodyTypeMessage, Net::IMAP::BodyTypeMultipart.
|
||||
# [ENVELOPE]
|
||||
# A Net::IMAP::Envelope object that describes the envelope
|
||||
# structure of a message.
|
||||
# [FLAGS]
|
||||
# A array of flag symbols that are set for this message. Flag symbols
|
||||
# are capitalized by String#capitalize.
|
||||
# [INTERNALDATE]
|
||||
# A string representing the internal date of the message.
|
||||
# [RFC822]
|
||||
# Equivalent to +BODY[]+.
|
||||
# [RFC822.HEADER]
|
||||
# Equivalent to +BODY.PEEK[HEADER]+.
|
||||
# [RFC822.SIZE]
|
||||
# A number expressing the [RFC-822] size of the message.
|
||||
# [RFC822.TEXT]
|
||||
# Equivalent to +BODY[TEXT]+.
|
||||
# [UID]
|
||||
# A number expressing the unique identifier of the message.
|
||||
#
|
||||
class FetchData < Struct.new(:seqno, :attr)
|
||||
end
|
||||
|
||||
# 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.
|
||||
#
|
||||
class Envelope < Struct.new(:date, :subject, :from, :sender, :reply_to,
|
||||
:to, :cc, :bcc, :in_reply_to, :message_id)
|
||||
end
|
||||
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
class Address < Struct.new(:name, :route, :mailbox, :host)
|
||||
end
|
||||
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
class ContentDisposition < Struct.new(:dsp_type, :param)
|
||||
end
|
||||
|
||||
# Net::IMAP::ThreadMember represents a thread-node returned
|
||||
# by Net::IMAP#thread.
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# seqno:: The sequence number of this message.
|
||||
#
|
||||
# children:: An array of Net::IMAP::ThreadMember objects for mail
|
||||
# items that are children of this in the thread.
|
||||
#
|
||||
class ThreadMember < Struct.new(:seqno, :children)
|
||||
end
|
||||
|
||||
# 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,
|
||||
:md5, :disposition, :language,
|
||||
:extension)
|
||||
def multipart?
|
||||
return false
|
||||
end
|
||||
|
||||
# Obsolete: use +subtype+ instead. Calling this will
|
||||
# generate a warning message to +stderr+, then return
|
||||
# the value of +subtype+.
|
||||
def media_subtype
|
||||
warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
|
||||
return subtype
|
||||
end
|
||||
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,
|
||||
:lines,
|
||||
:md5, :disposition, :language,
|
||||
:extension)
|
||||
def multipart?
|
||||
return false
|
||||
end
|
||||
|
||||
# Obsolete: use +subtype+ instead. Calling this will
|
||||
# generate a warning message to +stderr+, then return
|
||||
# the value of +subtype+.
|
||||
def media_subtype
|
||||
warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
|
||||
return subtype
|
||||
end
|
||||
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,
|
||||
:param, :content_id,
|
||||
:description, :encoding, :size,
|
||||
:envelope, :body, :lines,
|
||||
:md5, :disposition, :language,
|
||||
:extension)
|
||||
def multipart?
|
||||
return false
|
||||
end
|
||||
|
||||
# Obsolete: use +subtype+ instead. Calling this will
|
||||
# generate a warning message to +stderr+, then return
|
||||
# the value of +subtype+.
|
||||
def media_subtype
|
||||
warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
|
||||
return subtype
|
||||
end
|
||||
end
|
||||
|
||||
# Net::IMAP::BodyTypeAttachment represents attachment body structures
|
||||
# of messages.
|
||||
#
|
||||
# ==== Fields:
|
||||
#
|
||||
# media_type:: Returns the content media type name.
|
||||
#
|
||||
# subtype:: Returns +nil+.
|
||||
#
|
||||
# param:: Returns a hash that represents parameters.
|
||||
#
|
||||
# multipart?:: Returns false.
|
||||
#
|
||||
class BodyTypeAttachment < Struct.new(:media_type, :subtype,
|
||||
:param)
|
||||
def multipart?
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
# 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,
|
||||
:extension)
|
||||
def multipart?
|
||||
return true
|
||||
end
|
||||
|
||||
# Obsolete: use +subtype+ instead. Calling this will
|
||||
# generate a warning message to +stderr+, then return
|
||||
# the value of +subtype+.
|
||||
def media_subtype
|
||||
warn("media_subtype is obsolete, use subtype instead.\n", uplevel: 1)
|
||||
return subtype
|
||||
end
|
||||
end
|
||||
|
||||
class BodyTypeExtension < Struct.new(:media_type, :subtype,
|
||||
:params, :content_id,
|
||||
:description, :encoding, :size)
|
||||
def multipart?
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -80,7 +80,6 @@ DEFAULT_GEM_LIBS = %w[
|
|||
logger
|
||||
mutex_m
|
||||
net-http
|
||||
net-imap
|
||||
net-pop
|
||||
net-protocol
|
||||
net-smtp
|
||||
|
|
|
@ -1,856 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "net/imap"
|
||||
require "test/unit"
|
||||
|
||||
class IMAPTest < Test::Unit::TestCase
|
||||
CA_FILE = File.expand_path("../fixtures/cacert.pem", __dir__)
|
||||
SERVER_KEY = File.expand_path("../fixtures/server.key", __dir__)
|
||||
SERVER_CERT = File.expand_path("../fixtures/server.crt", __dir__)
|
||||
|
||||
def setup
|
||||
@do_not_reverse_lookup = Socket.do_not_reverse_lookup
|
||||
Socket.do_not_reverse_lookup = true
|
||||
@threads = []
|
||||
end
|
||||
|
||||
def teardown
|
||||
if !@threads.empty?
|
||||
assert_join_threads(@threads)
|
||||
end
|
||||
ensure
|
||||
Socket.do_not_reverse_lookup = @do_not_reverse_lookup
|
||||
end
|
||||
|
||||
if defined?(OpenSSL::SSL::SSLError)
|
||||
def test_imaps_unknown_ca
|
||||
assert_raise(OpenSSL::SSL::SSLError) do
|
||||
imaps_test do |port|
|
||||
begin
|
||||
Net::IMAP.new("localhost",
|
||||
:port => port,
|
||||
:ssl => true)
|
||||
rescue SystemCallError
|
||||
skip $!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_imaps_with_ca_file
|
||||
assert_nothing_raised do
|
||||
imaps_test do |port|
|
||||
begin
|
||||
Net::IMAP.new("localhost",
|
||||
:port => port,
|
||||
:ssl => { :ca_file => CA_FILE })
|
||||
rescue SystemCallError
|
||||
skip $!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_imaps_verify_none
|
||||
assert_nothing_raised do
|
||||
imaps_test do |port|
|
||||
Net::IMAP.new(server_addr,
|
||||
:port => port,
|
||||
:ssl => { :verify_mode => OpenSSL::SSL::VERIFY_NONE })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_imaps_post_connection_check
|
||||
assert_raise(OpenSSL::SSL::SSLError) do
|
||||
imaps_test do |port|
|
||||
# server_addr is different from the hostname in the certificate,
|
||||
# so the following code should raise a SSLError.
|
||||
Net::IMAP.new(server_addr,
|
||||
:port => port,
|
||||
:ssl => { :ca_file => CA_FILE })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if defined?(OpenSSL::SSL)
|
||||
def test_starttls
|
||||
imap = nil
|
||||
starttls_test do |port|
|
||||
imap = Net::IMAP.new("localhost", :port => port)
|
||||
imap.starttls(:ca_file => CA_FILE)
|
||||
imap
|
||||
end
|
||||
rescue SystemCallError
|
||||
skip $!
|
||||
ensure
|
||||
if imap && !imap.disconnected?
|
||||
imap.disconnect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def start_server
|
||||
th = Thread.new do
|
||||
yield
|
||||
end
|
||||
@threads << th
|
||||
sleep 0.1 until th.stop?
|
||||
end
|
||||
|
||||
def test_unexpected_eof
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sock.gets
|
||||
# sock.print("* BYE terminating connection\r\n")
|
||||
# sock.print("RUBY0001 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
assert_raise(EOFError) do
|
||||
imap.logout
|
||||
end
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
def test_idle
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
requests = []
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("+ idling\r\n")
|
||||
sock.print("* 3 EXISTS\r\n")
|
||||
sock.print("* 2 EXPUNGE\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("RUBY0001 OK IDLE terminated\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
responses = []
|
||||
imap.idle do |res|
|
||||
responses.push(res)
|
||||
if res.name == "EXPUNGE"
|
||||
imap.idle_done
|
||||
end
|
||||
end
|
||||
assert_equal(3, responses.length)
|
||||
assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
|
||||
assert_equal("EXISTS", responses[1].name)
|
||||
assert_equal(3, responses[1].data)
|
||||
assert_equal("EXPUNGE", responses[2].name)
|
||||
assert_equal(2, responses[2].data)
|
||||
assert_equal(2, requests.length)
|
||||
assert_equal("RUBY0001 IDLE\r\n", requests[0])
|
||||
assert_equal("DONE\r\n", requests[1])
|
||||
imap.logout
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_during_idle
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
requests = []
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("+ idling\r\n")
|
||||
sock.print("* 3 EXISTS\r\n")
|
||||
sock.print("* 2 EXPUNGE\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("RUBY0001 OK IDLE terminated\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
begin
|
||||
th = Thread.current
|
||||
m = Monitor.new
|
||||
in_idle = false
|
||||
exception_raised = false
|
||||
c = m.new_cond
|
||||
raiser = Thread.start do
|
||||
m.synchronize do
|
||||
until in_idle
|
||||
c.wait(0.1)
|
||||
end
|
||||
end
|
||||
th.raise(Interrupt)
|
||||
m.synchronize do
|
||||
exception_raised = true
|
||||
c.signal
|
||||
end
|
||||
end
|
||||
@threads << raiser
|
||||
imap.idle do |res|
|
||||
m.synchronize do
|
||||
in_idle = true
|
||||
c.signal
|
||||
until exception_raised
|
||||
c.wait(0.1)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Interrupt
|
||||
end
|
||||
assert_equal(2, requests.length)
|
||||
assert_equal("RUBY0001 IDLE\r\n", requests[0])
|
||||
assert_equal("DONE\r\n", requests[1])
|
||||
imap.logout
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
raiser.kill unless in_idle
|
||||
end
|
||||
end
|
||||
|
||||
def test_idle_done_not_during_idle
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sleep 0.1
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
assert_raise(Net::IMAP::Error) do
|
||||
imap.idle_done
|
||||
end
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
def test_idle_timeout
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
requests = []
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("+ idling\r\n")
|
||||
sock.print("* 3 EXISTS\r\n")
|
||||
sock.print("* 2 EXPUNGE\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("RUBY0001 OK IDLE terminated\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
responses = []
|
||||
Thread.pass
|
||||
imap.idle(0.2) do |res|
|
||||
responses.push(res)
|
||||
end
|
||||
# There is no guarantee that this thread has received all the responses,
|
||||
# so check the response length.
|
||||
if responses.length > 0
|
||||
assert_instance_of(Net::IMAP::ContinuationRequest, responses[0])
|
||||
if responses.length > 1
|
||||
assert_equal("EXISTS", responses[1].name)
|
||||
assert_equal(3, responses[1].data)
|
||||
if responses.length > 2
|
||||
assert_equal("EXPUNGE", responses[2].name)
|
||||
assert_equal(2, responses[2].data)
|
||||
end
|
||||
end
|
||||
end
|
||||
# Also, there is no guarantee that the server thread has stored
|
||||
# all the requests into the array, so check the length.
|
||||
if requests.length > 0
|
||||
assert_equal("RUBY0001 IDLE\r\n", requests[0])
|
||||
if requests.length > 1
|
||||
assert_equal("DONE\r\n", requests[1])
|
||||
end
|
||||
end
|
||||
imap.logout
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
def test_unexpected_bye
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK Gimap ready for requests from 75.101.246.151 33if2752585qyk.26\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE System Error 33if2752585qyk.26\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
assert_raise(Net::IMAP::ByeResponseError) do
|
||||
imap.login("user", "password")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_exception_during_shutdown
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0001 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
imap.instance_eval do
|
||||
def @sock.shutdown(*args)
|
||||
super
|
||||
ensure
|
||||
raise "error"
|
||||
end
|
||||
end
|
||||
imap.logout
|
||||
ensure
|
||||
assert_raise(RuntimeError) do
|
||||
imap.disconnect
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_connection_closed_during_idle
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
requests = []
|
||||
sock = nil
|
||||
threads = []
|
||||
started = false
|
||||
threads << Thread.start do
|
||||
started = true
|
||||
begin
|
||||
sock = server.accept
|
||||
sock.print("* OK test server\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("+ idling\r\n")
|
||||
rescue IOError # sock is closed by another thread
|
||||
ensure
|
||||
server.close
|
||||
end
|
||||
end
|
||||
sleep 0.1 until started
|
||||
threads << Thread.start do
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
begin
|
||||
m = Monitor.new
|
||||
in_idle = false
|
||||
closed = false
|
||||
c = m.new_cond
|
||||
threads << Thread.start do
|
||||
m.synchronize do
|
||||
until in_idle
|
||||
c.wait(0.1)
|
||||
end
|
||||
end
|
||||
sock.close
|
||||
m.synchronize do
|
||||
closed = true
|
||||
c.signal
|
||||
end
|
||||
end
|
||||
assert_raise(EOFError) do
|
||||
imap.idle do |res|
|
||||
m.synchronize do
|
||||
in_idle = true
|
||||
c.signal
|
||||
until closed
|
||||
c.wait(0.1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
assert_equal(1, requests.length)
|
||||
assert_equal("RUBY0001 IDLE\r\n", requests[0])
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
assert_join_threads(threads)
|
||||
ensure
|
||||
if sock && !sock.closed?
|
||||
sock.close
|
||||
end
|
||||
end
|
||||
|
||||
def test_connection_closed_without_greeting
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
h = {
|
||||
server: server,
|
||||
port: port,
|
||||
server_created: {
|
||||
server: server.inspect,
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
}
|
||||
}
|
||||
net_imap = Class.new(Net::IMAP) do
|
||||
@@h = h
|
||||
def tcp_socket(host, port)
|
||||
@@h[:in_tcp_socket] = {
|
||||
host: host,
|
||||
port: port,
|
||||
server: @@h[:server].inspect,
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
}
|
||||
#super
|
||||
s = Socket.tcp(host, port, :connect_timeout => @open_timeout)
|
||||
@@h[:in_tcp_socket_2] = {
|
||||
s: s.inspect,
|
||||
local_address: s.local_address,
|
||||
remote_address: s.remote_address,
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
}
|
||||
s.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, true)
|
||||
s
|
||||
end
|
||||
end
|
||||
start_server do
|
||||
begin
|
||||
h[:in_start_server_before_accept] = {
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
}
|
||||
sock = server.accept
|
||||
h[:in_start_server] = {
|
||||
sock_addr: sock.addr,
|
||||
sock_peeraddr: sock.peeraddr,
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
sockets: ObjectSpace.each_object(BasicSocket).map{|s| [s.inspect, connect_address: (s.connect_address rescue nil).inspect, local_address: (s.local_address rescue nil).inspect, remote_address: (s.remote_address rescue nil).inspect] },
|
||||
}
|
||||
sock.close
|
||||
h[:in_start_server_sock_closed] = {
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
}
|
||||
ensure
|
||||
server.close
|
||||
end
|
||||
end
|
||||
assert_raise(Net::IMAP::Error) do
|
||||
#Net::IMAP.new(server_addr, :port => port)
|
||||
if true
|
||||
net_imap.new(server_addr, :port => port)
|
||||
else
|
||||
# for testing debug print
|
||||
begin
|
||||
net_imap.new(server_addr, :port => port)
|
||||
rescue Net::IMAP::Error
|
||||
raise Errno::EINVAL
|
||||
end
|
||||
end
|
||||
rescue SystemCallError => e # for debug on OpenCSW
|
||||
h[:in_rescue] = {
|
||||
e: e,
|
||||
server_addr: server_addr,
|
||||
t: Process.clock_gettime(Process::CLOCK_MONOTONIC),
|
||||
}
|
||||
require 'pp'
|
||||
raise(PP.pp(h, +''))
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_port
|
||||
assert_equal(143, Net::IMAP.default_port)
|
||||
assert_equal(143, Net::IMAP.default_imap_port)
|
||||
assert_equal(993, Net::IMAP.default_tls_port)
|
||||
assert_equal(993, Net::IMAP.default_ssl_port)
|
||||
assert_equal(993, Net::IMAP.default_imaps_port)
|
||||
end
|
||||
|
||||
def test_send_invalid_number
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sock.gets
|
||||
sock.print("RUBY0001 OK TEST completed\r\n")
|
||||
sock.gets
|
||||
sock.print("RUBY0002 OK TEST completed\r\n")
|
||||
sock.gets
|
||||
sock.print("RUBY0003 OK TEST completed\r\n")
|
||||
sock.gets
|
||||
sock.print("RUBY0004 OK TEST completed\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0005 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
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
|
||||
imap.disconnect
|
||||
end
|
||||
end
|
||||
|
||||
def test_send_literal
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
requests = []
|
||||
literal = nil
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
line = sock.gets
|
||||
requests.push(line)
|
||||
size = line.slice(/{(\d+)}\r\n/, 1).to_i
|
||||
sock.print("+ Ready for literal data\r\n")
|
||||
literal = sock.read(size)
|
||||
requests.push(sock.gets)
|
||||
sock.print("RUBY0001 OK TEST completed\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
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)
|
||||
assert_equal(")\r\n", requests[1])
|
||||
imap.logout
|
||||
ensure
|
||||
imap.disconnect
|
||||
end
|
||||
end
|
||||
|
||||
def test_disconnect
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0001 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
imap.logout
|
||||
imap.disconnect
|
||||
assert_equal(true, imap.disconnected?)
|
||||
imap.disconnect
|
||||
assert_equal(true, imap.disconnected?)
|
||||
ensure
|
||||
imap.disconnect if imap && !imap.disconnected?
|
||||
end
|
||||
end
|
||||
|
||||
def test_append
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
mail = <<EOF.gsub(/\n/, "\r\n")
|
||||
From: shugo@example.com
|
||||
To: matz@example.com
|
||||
Subject: hello
|
||||
|
||||
hello world
|
||||
EOF
|
||||
requests = []
|
||||
received_mail = nil
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
line = sock.gets
|
||||
requests.push(line)
|
||||
size = line.slice(/{(\d+)}\r\n/, 1).to_i
|
||||
sock.print("+ Ready for literal data\r\n")
|
||||
received_mail = sock.read(size)
|
||||
sock.gets
|
||||
sock.print("RUBY0001 OK APPEND completed\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
imap.append("INBOX", mail)
|
||||
assert_equal(1, requests.length)
|
||||
assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0])
|
||||
assert_equal(mail, received_mail)
|
||||
imap.logout
|
||||
assert_equal(2, requests.length)
|
||||
assert_equal("RUBY0002 LOGOUT\r\n", requests[1])
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
def test_append_fail
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
mail = <<EOF.gsub(/\n/, "\r\n")
|
||||
From: shugo@example.com
|
||||
To: matz@example.com
|
||||
Subject: hello
|
||||
|
||||
hello world
|
||||
EOF
|
||||
requests = []
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("RUBY0001 NO Mailbox doesn't exist\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
assert_raise(Net::IMAP::NoResponseError) do
|
||||
imap.append("INBOX", mail)
|
||||
end
|
||||
assert_equal(1, requests.length)
|
||||
assert_equal("RUBY0001 APPEND INBOX {#{mail.size}}\r\n", requests[0])
|
||||
imap.logout
|
||||
assert_equal(2, requests.length)
|
||||
assert_equal("RUBY0002 LOGOUT\r\n", requests[1])
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
def test_id
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
requests = Queue.new
|
||||
server_id = {"name" => "test server", "version" => "v0.1.0"}
|
||||
server_id_str = '("name" "test server" "version" "v0.1.0")'
|
||||
@threads << Thread.start do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
requests.push(sock.gets)
|
||||
# RFC 2971 very clearly states (in section 3.2):
|
||||
# "a server MUST send a tagged ID response to an ID command."
|
||||
# And yet... some servers report ID capability but won't the response.
|
||||
sock.print("RUBY0001 OK ID completed\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("* ID #{server_id_str}\r\n")
|
||||
sock.print("RUBY0002 OK ID completed\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("* ID #{server_id_str}\r\n")
|
||||
sock.print("RUBY0003 OK ID completed\r\n")
|
||||
requests.push(sock.gets)
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0004 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
imap = Net::IMAP.new(server_addr, :port => port)
|
||||
resp = imap.id
|
||||
assert_equal(nil, resp)
|
||||
assert_equal("RUBY0001 ID NIL\r\n", requests.pop)
|
||||
resp = imap.id({})
|
||||
assert_equal(server_id, resp)
|
||||
assert_equal("RUBY0002 ID ()\r\n", requests.pop)
|
||||
resp = imap.id("name" => "test client", "version" => "latest")
|
||||
assert_equal(server_id, resp)
|
||||
assert_equal("RUBY0003 ID (\"name\" \"test client\" \"version\" \"latest\")\r\n",
|
||||
requests.pop)
|
||||
imap.logout
|
||||
assert_equal("RUBY0004 LOGOUT\r\n", requests.pop)
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def imaps_test
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.ca_file = CA_FILE
|
||||
ctx.key = File.open(SERVER_KEY) { |f|
|
||||
OpenSSL::PKey::RSA.new(f)
|
||||
}
|
||||
ctx.cert = File.open(SERVER_CERT) { |f|
|
||||
OpenSSL::X509::Certificate.new(f)
|
||||
}
|
||||
ssl_server = OpenSSL::SSL::SSLServer.new(server, ctx)
|
||||
started = false
|
||||
ths = Thread.start do
|
||||
Thread.current.report_on_exception = false # always join-ed
|
||||
begin
|
||||
started = true
|
||||
sock = ssl_server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0001 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
end
|
||||
rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ECONNABORTED
|
||||
end
|
||||
end
|
||||
sleep 0.1 until started
|
||||
begin
|
||||
begin
|
||||
imap = yield(port)
|
||||
imap.logout
|
||||
ensure
|
||||
imap.disconnect if imap
|
||||
end
|
||||
ensure
|
||||
ssl_server.close
|
||||
ths.join
|
||||
end
|
||||
end
|
||||
|
||||
def starttls_test
|
||||
server = create_tcp_server
|
||||
port = server.addr[1]
|
||||
start_server do
|
||||
sock = server.accept
|
||||
begin
|
||||
sock.print("* OK test server\r\n")
|
||||
sock.gets
|
||||
sock.print("RUBY0001 OK completed\r\n")
|
||||
ctx = OpenSSL::SSL::SSLContext.new
|
||||
ctx.ca_file = CA_FILE
|
||||
ctx.key = File.open(SERVER_KEY) { |f|
|
||||
OpenSSL::PKey::RSA.new(f)
|
||||
}
|
||||
ctx.cert = File.open(SERVER_CERT) { |f|
|
||||
OpenSSL::X509::Certificate.new(f)
|
||||
}
|
||||
sock = OpenSSL::SSL::SSLSocket.new(sock, ctx)
|
||||
sock.sync_close = true
|
||||
sock.accept
|
||||
sock.gets
|
||||
sock.print("* BYE terminating connection\r\n")
|
||||
sock.print("RUBY0002 OK LOGOUT completed\r\n")
|
||||
ensure
|
||||
sock.close
|
||||
server.close
|
||||
end
|
||||
end
|
||||
begin
|
||||
imap = yield(port)
|
||||
imap.logout if !imap.disconnected?
|
||||
ensure
|
||||
imap.disconnect if imap && !imap.disconnected?
|
||||
end
|
||||
end
|
||||
|
||||
def create_tcp_server
|
||||
return TCPServer.new(server_addr, 0)
|
||||
end
|
||||
|
||||
def server_addr
|
||||
Addrinfo.tcp("localhost", 0).ip_address
|
||||
end
|
||||
end
|
|
@ -1,23 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "net/imap"
|
||||
require "test/unit"
|
||||
|
||||
class IMAPAuthenticatorsTest < Test::Unit::TestCase
|
||||
|
||||
PLAIN = Net::IMAP::PlainAuthenticator
|
||||
|
||||
def test_plain
|
||||
assert_equal("\0authc\0passwd",
|
||||
PLAIN.new("authc", "passwd").process(nil))
|
||||
assert_equal("authz\0user\0pass",
|
||||
PLAIN.new("user", "pass", authzid: "authz").process(nil))
|
||||
end
|
||||
|
||||
def test_plain_no_null_chars
|
||||
assert_raise(ArgumentError) { PLAIN.new("bad\0user", "pass") }
|
||||
assert_raise(ArgumentError) { PLAIN.new("user", "bad\0pass") }
|
||||
assert_raise(ArgumentError) { PLAIN.new("u", "p", authzid: "bad\0authz") }
|
||||
end
|
||||
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "net/imap"
|
||||
require "test/unit"
|
||||
|
||||
class IMAPDataEncodingTest < Test::Unit::TestCase
|
||||
|
||||
def test_encode_utf7
|
||||
assert_equal("foo", Net::IMAP.encode_utf7("foo"))
|
||||
assert_equal("&-", Net::IMAP.encode_utf7("&"))
|
||||
|
||||
utf8 = "\357\274\241\357\274\242\357\274\243".dup.force_encoding("UTF-8")
|
||||
s = Net::IMAP.encode_utf7(utf8)
|
||||
assert_equal("&,yH,Iv8j-", s)
|
||||
s = Net::IMAP.encode_utf7("foo&#{utf8}-bar".encode("EUC-JP"))
|
||||
assert_equal("foo&-&,yH,Iv8j--bar", s)
|
||||
|
||||
utf8 = "\343\201\202&".dup.force_encoding("UTF-8")
|
||||
s = Net::IMAP.encode_utf7(utf8)
|
||||
assert_equal("&MEI-&-", s)
|
||||
s = Net::IMAP.encode_utf7(utf8.encode("EUC-JP"))
|
||||
assert_equal("&MEI-&-", s)
|
||||
end
|
||||
|
||||
def test_decode_utf7
|
||||
assert_equal("&", Net::IMAP.decode_utf7("&-"))
|
||||
assert_equal("&-", Net::IMAP.decode_utf7("&--"))
|
||||
|
||||
s = Net::IMAP.decode_utf7("&,yH,Iv8j-")
|
||||
utf8 = "\357\274\241\357\274\242\357\274\243".dup.force_encoding("UTF-8")
|
||||
assert_equal(utf8, s)
|
||||
end
|
||||
|
||||
def test_format_date
|
||||
time = Time.mktime(2009, 7, 24)
|
||||
s = Net::IMAP.format_date(time)
|
||||
assert_equal("24-Jul-2009", s)
|
||||
end
|
||||
|
||||
def test_format_datetime
|
||||
time = Time.mktime(2009, 7, 24, 1, 23, 45)
|
||||
s = Net::IMAP.format_datetime(time)
|
||||
assert_match(/\A24-Jul-2009 01:23 [+\-]\d{4}\z/, s)
|
||||
end
|
||||
|
||||
end
|
|
@ -1,389 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "net/imap"
|
||||
require "test/unit"
|
||||
|
||||
class IMAPResponseParserTest < Test::Unit::TestCase
|
||||
def setup
|
||||
@do_not_reverse_lookup = Socket.do_not_reverse_lookup
|
||||
Socket.do_not_reverse_lookup = true
|
||||
if Net::IMAP.respond_to?(:max_flag_count)
|
||||
@max_flag_count = Net::IMAP.max_flag_count
|
||||
Net::IMAP.max_flag_count = 3
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
Socket.do_not_reverse_lookup = @do_not_reverse_lookup
|
||||
if Net::IMAP.respond_to?(:max_flag_count)
|
||||
Net::IMAP.max_flag_count = @max_flag_count
|
||||
end
|
||||
end
|
||||
|
||||
def test_flag_list_too_many_flags
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
assert_nothing_raised do
|
||||
3.times do |i|
|
||||
parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* LIST (\\Foo#{i}) "." "INBOX"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
assert_raise(Net::IMAP::FlagCountError) do
|
||||
parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* LIST (\\Foo3) "." "INBOX"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
|
||||
def test_flag_list_many_same_flags
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
assert_nothing_raised do
|
||||
100.times do
|
||||
parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* LIST (\\Foo) "." "INBOX"
|
||||
EOF
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_flag_xlist_inbox
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* XLIST (\\Inbox) "." "INBOX"
|
||||
EOF
|
||||
assert_equal [:Inbox], response.data.attr
|
||||
end
|
||||
|
||||
def test_resp_text_code
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* OK [CLOSED] Previous mailbox closed.
|
||||
EOF
|
||||
assert_equal "CLOSED", response.data.code.name
|
||||
end
|
||||
|
||||
def test_search_response
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* SEARCH
|
||||
EOF
|
||||
assert_equal [], response.data
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* SEARCH 1
|
||||
EOF
|
||||
assert_equal [1], response.data
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* SEARCH 1 2 3
|
||||
EOF
|
||||
assert_equal [1, 2, 3], response.data
|
||||
end
|
||||
|
||||
def test_search_response_of_yahoo
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* SEARCH 1\s
|
||||
EOF
|
||||
assert_equal [1], response.data
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* SEARCH 1 2 3\s
|
||||
EOF
|
||||
assert_equal [1, 2, 3], response.data
|
||||
end
|
||||
|
||||
def test_msg_att_extra_space
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 1 FETCH (UID 92285)
|
||||
EOF
|
||||
assert_equal 92285, response.data.attr["UID"]
|
||||
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 1 FETCH (UID 92285 )
|
||||
EOF
|
||||
assert_equal 92285, response.data.attr["UID"]
|
||||
end
|
||||
|
||||
def test_msg_att_parse_error
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
e = assert_raise(Net::IMAP::ResponseParseError) {
|
||||
parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 123 FETCH (UNKNOWN 92285)
|
||||
EOF
|
||||
}
|
||||
assert_match(/ for \{123\}/, e.message)
|
||||
end
|
||||
|
||||
def test_msg_att_rfc822_text
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 123 FETCH (RFC822 {5}
|
||||
foo
|
||||
)
|
||||
EOF
|
||||
assert_equal("foo\r\n", response.data.attr["RFC822"])
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 123 FETCH (RFC822[] {5}
|
||||
foo
|
||||
)
|
||||
EOF
|
||||
assert_equal("foo\r\n", response.data.attr["RFC822"])
|
||||
end
|
||||
|
||||
# [Bug #6397] [ruby-core:44849]
|
||||
def test_body_type_attachment
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 980 FETCH (UID 2862 BODYSTRUCTURE ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 416 21 NIL NIL NIL)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "7BIT" 1493 32 NIL NIL NIL) "ALTERNATIVE" ("BOUNDARY" "Boundary_(ID_IaecgfnXwG5bn3x8lIeGIQ)") NIL NIL)("MESSAGE" "RFC822" ("NAME" "Fw_ ____ _____ ____.eml") NIL NIL "7BIT" 1980088 NIL ("ATTACHMENT" ("FILENAME" "Fw_ ____ _____ ____.eml")) NIL) "MIXED" ("BOUNDARY" "Boundary_(ID_eDdLc/j0mBIzIlR191pHjA)") NIL NIL))
|
||||
EOF
|
||||
assert_equal("Fw_ ____ _____ ____.eml",
|
||||
response.data.attr["BODYSTRUCTURE"].parts[1].body.param["FILENAME"])
|
||||
end
|
||||
|
||||
def assert_parseable(s)
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
parser.parse(s.gsub(/\n/, "\r\n"))
|
||||
end
|
||||
|
||||
# [Bug #7146]
|
||||
def test_msg_delivery_status
|
||||
# This was part of a larger response that caused crashes, but this was the
|
||||
# minimal test case to demonstrate it
|
||||
assert_parseable <<EOF
|
||||
* 4902 FETCH (BODY (("MESSAGE" "DELIVERY-STATUS" NIL NIL NIL "7BIT" 324) "REPORT"))
|
||||
EOF
|
||||
end
|
||||
|
||||
# [Bug #7147]
|
||||
def test_msg_with_message_rfc822_attachment
|
||||
assert_parseable <<EOF
|
||||
* 5441 FETCH (BODY ((("TEXT" "PLAIN" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 69 1)("TEXT" "HTML" ("CHARSET" "iso-8859-1") NIL NIL "QUOTED-PRINTABLE" 455 12) "ALTERNATIVE")("MESSAGE" "RFC822" ("NAME" "ATT00026.eml") NIL NIL "7BIT" 4079755) "MIXED"))
|
||||
EOF
|
||||
end
|
||||
|
||||
# [Bug #7153]
|
||||
def test_msg_body_mixed
|
||||
assert_parseable <<EOF
|
||||
* 1038 FETCH (BODY ("MIXED"))
|
||||
EOF
|
||||
end
|
||||
|
||||
# [Bug #8167]
|
||||
def test_msg_delivery_status_with_extra_data
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* 29021 FETCH (RFC822.SIZE 3162 UID 113622 RFC822.HEADER {1155}
|
||||
Return-path: <>
|
||||
Envelope-to: info@xxxxxxxx.si
|
||||
Delivery-date: Tue, 26 Mar 2013 12:42:58 +0100
|
||||
Received: from mail by xxxx.xxxxxxxxxxx.net with spam-scanned (Exim 4.76)
|
||||
id 1UKSHI-000Cwl-AR
|
||||
for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:58 +0100
|
||||
X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on xxxx.xxxxxxxxxxx.net
|
||||
X-Spam-Level: **
|
||||
X-Spam-Status: No, score=2.1 required=7.0 tests=DKIM_ADSP_NXDOMAIN,RDNS_NONE
|
||||
autolearn=no version=3.3.1
|
||||
Received: from [xx.xxx.xxx.xx] (port=56890 helo=xxxxxx.localdomain)
|
||||
by xxxx.xxxxxxxxxxx.net with esmtp (Exim 4.76)
|
||||
id 1UKSHI-000Cwi-9j
|
||||
for info@xxxxxxxx.si; Tue, 26 Mar 2013 12:42:56 +0100
|
||||
Received: by xxxxxx.localdomain (Postfix)
|
||||
id 72725BEA64A; Tue, 26 Mar 2013 12:42:55 +0100 (CET)
|
||||
Date: Tue, 26 Mar 2013 12:42:55 +0100 (CET)
|
||||
From: MAILER-DAEMON@xxxxxx.localdomain (Mail Delivery System)
|
||||
Subject: Undelivered Mail Returned to Sender
|
||||
To: info@xxxxxxxx.si
|
||||
Auto-Submitted: auto-replied
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/report; report-type=delivery-status;
|
||||
boundary="27797BEA649.1364298175/xxxxxx.localdomain"
|
||||
Message-Id: <20130326114255.72725BEA64A@xxxxxx.localdomain>
|
||||
|
||||
BODYSTRUCTURE (("text" "plain" ("charset" "us-ascii") NIL "Notification" "7bit" 510 14 NIL NIL NIL NIL)("message" "delivery-status" NIL NIL "Delivery report" "7bit" 410 NIL NIL NIL NIL)("text" "rfc822-headers" ("charset" "us-ascii") NIL "Undelivered Message Headers" "7bit" 612 15 NIL NIL NIL NIL) "report" ("report-type" "delivery-status" "boundary" "27797BEA649.1364298175/xxxxxx.localdomain") NIL NIL NIL))
|
||||
EOF
|
||||
delivery_status = response.data.attr["BODYSTRUCTURE"].parts[1]
|
||||
assert_equal("MESSAGE", delivery_status.media_type)
|
||||
assert_equal("DELIVERY-STATUS", delivery_status.subtype)
|
||||
assert_equal(nil, delivery_status.param)
|
||||
assert_equal(nil, delivery_status.content_id)
|
||||
assert_equal("Delivery report", delivery_status.description)
|
||||
assert_equal("7BIT", delivery_status.encoding)
|
||||
assert_equal(410, delivery_status.size)
|
||||
end
|
||||
|
||||
# [Bug #8281]
|
||||
def test_acl
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse(<<EOF.gsub(/\n/, "\r\n"))
|
||||
* ACL "INBOX/share" "imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com" lrswickxteda
|
||||
EOF
|
||||
assert_equal("ACL", response.name)
|
||||
assert_equal(1, response.data.length)
|
||||
assert_equal("INBOX/share", response.data[0].mailbox)
|
||||
assert_equal("imshare2copy1366146467@xxxxxxxxxxxxxxxxxx.com",
|
||||
response.data[0].user)
|
||||
assert_equal("lrswickxteda", response.data[0].rights)
|
||||
end
|
||||
|
||||
# [Bug #8415]
|
||||
def test_capability
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
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)
|
||||
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)
|
||||
response = parser.parse("* OK [CAPABILITY IMAP4rev1 SASL-IR 1234 NIL THIS+THAT + AUTH=PLAIN ID] IMAP4rev1 Hello\r\n")
|
||||
assert_equal("OK", response.name)
|
||||
assert_equal("IMAP4rev1 Hello", response.data.text)
|
||||
code = response.data.code
|
||||
assert_equal("CAPABILITY", code.name)
|
||||
assert_equal(
|
||||
["IMAP4REV1", "SASL-IR", "1234", "NIL", "THIS+THAT", "+", "AUTH=PLAIN", "ID"],
|
||||
code.data
|
||||
)
|
||||
end
|
||||
|
||||
def test_id
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("* ID NIL\r\n")
|
||||
assert_equal("ID", response.name)
|
||||
assert_equal(nil, response.data)
|
||||
response = parser.parse("* ID (\"name\" \"GImap\" \"vendor\" \"Google, Inc.\" \"support-url\" NIL)\r\n")
|
||||
assert_equal("ID", response.name)
|
||||
assert_equal("GImap", response.data["name"])
|
||||
assert_equal("Google, Inc.", response.data["vendor"])
|
||||
assert_equal(nil, response.data.fetch("support-url"))
|
||||
end
|
||||
|
||||
def test_mixed_boundary
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("* 2688 FETCH (UID 179161 BODYSTRUCTURE (" \
|
||||
"(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"iso-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 200 4 NIL NIL NIL)" \
|
||||
"(\"MESSAGE\" \"DELIVERY-STATUS\" NIL NIL NIL \"7BIT\" 318 NIL NIL NIL)" \
|
||||
"(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"7BIT\" 2177" \
|
||||
" (\"Tue, 11 May 2010 18:28:16 -0400\" \"Re: Welcome letter\" (" \
|
||||
"(\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
|
||||
"((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
|
||||
"((\"David\" NIL \"info\" \"xxxxxxxx.si\")) " \
|
||||
"((\"Doretha\" NIL \"doretha.info\" \"xxxxxxxx.si\")) " \
|
||||
"NIL NIL " \
|
||||
"\"<AC1D15E06EA82F47BDE18E851CC32F330717704E@localdomain>\" " \
|
||||
"\"<AANLkTikKMev1I73L2E7XLjRs67IHrEkb23f7ZPmD4S_9@localdomain>\")" \
|
||||
" (\"MIXED\" (\"BOUNDARY\" \"000e0cd29212e3e06a0486590ae2\") NIL NIL)" \
|
||||
" 37 NIL NIL NIL)" \
|
||||
" \"REPORT\" (\"BOUNDARY\" \"16DuG.4XbaNOvCi.9ggvq.8Ipnyp3\" \"REPORT-TYPE\" \"delivery-status\") NIL NIL))\r\n")
|
||||
empty_part = response.data.attr['BODYSTRUCTURE'].parts[2]
|
||||
assert_equal(empty_part.lines, 37)
|
||||
assert_equal(empty_part.body.media_type, 'MULTIPART')
|
||||
assert_equal(empty_part.body.subtype, 'MIXED')
|
||||
assert_equal(empty_part.body.param['BOUNDARY'], '000e0cd29212e3e06a0486590ae2')
|
||||
end
|
||||
|
||||
# [Bug #10112]
|
||||
def test_search_modseq
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("* SEARCH 87216 87221 (MODSEQ 7667567)\r\n")
|
||||
assert_equal("SEARCH", response.name)
|
||||
assert_equal([87216, 87221], response.data)
|
||||
end
|
||||
|
||||
# [Bug #11128]
|
||||
def test_body_ext_mpart_without_lang
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("* 4 FETCH (BODY (((\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"7bit\" 257 9 NIL NIL NIL NIL)(\"text\" \"html\" (\"charset\" \"utf-8\") NIL NIL \"quoted-printable\" 655 9 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"001a1137a5047848dd05157ddaa1\") NIL)(\"application\" \"pdf\" (\"name\" \"test.xml\" \"x-apple-part-url\" \"9D00D9A2-98AB-4EFB-85BA-FB255F8BF3D7\") NIL NIL \"base64\" 4383638 NIL (\"attachment\" (\"filename\" \"test.xml\")) NIL NIL) \"mixed\" (\"boundary\" \"001a1137a5047848e405157ddaa3\") NIL))\r\n")
|
||||
assert_equal("FETCH", response.name)
|
||||
body = response.data.attr["BODY"]
|
||||
assert_equal(nil, body.parts[0].disposition)
|
||||
assert_equal(nil, body.parts[0].language)
|
||||
assert_equal("ATTACHMENT", body.parts[1].disposition.dsp_type)
|
||||
assert_equal("test.xml", body.parts[1].disposition.param["FILENAME"])
|
||||
assert_equal(nil, body.parts[1].language)
|
||||
end
|
||||
|
||||
# [Bug #13649]
|
||||
def test_status
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1234)\r\n")
|
||||
assert_equal("STATUS", response.name)
|
||||
assert_equal("INBOX", response.data.mailbox)
|
||||
assert_equal(1234, response.data.attr["UIDVALIDITY"])
|
||||
response = parser.parse("* STATUS INBOX (UIDNEXT 1 UIDVALIDITY 1234) \r\n")
|
||||
assert_equal("STATUS", response.name)
|
||||
assert_equal("INBOX", response.data.mailbox)
|
||||
assert_equal(1234, response.data.attr["UIDVALIDITY"])
|
||||
end
|
||||
|
||||
# [Bug #10119]
|
||||
def test_msg_att_modseq_data
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("* 1 FETCH (FLAGS (\Seen) MODSEQ (12345) UID 5)\r\n")
|
||||
assert_equal(12345, response.data.attr["MODSEQ"])
|
||||
end
|
||||
|
||||
def test_msg_rfc3501_response_text_with_T_LBRA
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("RUBY0004 OK [READ-WRITE] [Gmail]/Sent Mail selected. (Success)\r\n")
|
||||
assert_equal("RUBY0004", response.tag)
|
||||
assert_equal("READ-WRITE", response.data.code.name)
|
||||
assert_equal("[Gmail]/Sent Mail selected. (Success)", response.data.text)
|
||||
end
|
||||
|
||||
def test_msg_rfc3501_response_text_with_BADCHARSET_astrings
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("t BAD [BADCHARSET (US-ASCII \"[astring with brackets]\")] unsupported charset foo.\r\n")
|
||||
assert_equal("t", response.tag)
|
||||
assert_equal("unsupported charset foo.", response.data.text)
|
||||
assert_equal("BADCHARSET", response.data.code.name)
|
||||
end
|
||||
|
||||
def test_continuation_request_without_response_text
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = parser.parse("+\r\n")
|
||||
assert_instance_of(Net::IMAP::ContinuationRequest, response)
|
||||
assert_equal(nil, response.data.code)
|
||||
assert_equal("", response.data.text)
|
||||
end
|
||||
|
||||
def test_ignored_response
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
response = nil
|
||||
assert_nothing_raised do
|
||||
response = parser.parse("* NOOP\r\n")
|
||||
end
|
||||
assert_instance_of(Net::IMAP::IgnoredResponse, response)
|
||||
end
|
||||
|
||||
def test_namespace
|
||||
parser = Net::IMAP::ResponseParser.new
|
||||
# RFC2342 Example 5.1
|
||||
response = parser.parse(%Q{* NAMESPACE (("" "/")) NIL NIL\r\n})
|
||||
assert_equal("NAMESPACE", response.name)
|
||||
assert_equal([Net::IMAP::Namespace.new("", "/", {})], response.data.personal)
|
||||
assert_equal([], response.data.other)
|
||||
assert_equal([], response.data.shared)
|
||||
# RFC2342 Example 5.4
|
||||
response = parser.parse(%Q{* NAMESPACE (("" "/")) (("~" "/")) (("#shared/" "/")} +
|
||||
%Q{ ("#public/" "/") ("#ftp/" "/") ("#news." "."))\r\n})
|
||||
assert_equal("NAMESPACE", response.name)
|
||||
assert_equal([Net::IMAP::Namespace.new("", "/", {})], response.data.personal)
|
||||
assert_equal([Net::IMAP::Namespace.new("~", "/", {})], response.data.other)
|
||||
assert_equal(
|
||||
[
|
||||
Net::IMAP::Namespace.new("#shared/", "/", {}),
|
||||
Net::IMAP::Namespace.new("#public/", "/", {}),
|
||||
Net::IMAP::Namespace.new("#ftp/", "/", {}),
|
||||
Net::IMAP::Namespace.new("#news.", ".", {}),
|
||||
],
|
||||
response.data.shared
|
||||
)
|
||||
# RFC2342 Example 5.6
|
||||
response = parser.parse(%Q{* NAMESPACE (("" "/") ("#mh/" "/" "X-PARAM" ("FLAG1" "FLAG2"))) NIL NIL\r\n})
|
||||
assert_equal("NAMESPACE", response.name)
|
||||
namespace = response.data.personal.last
|
||||
assert_equal("#mh/", namespace.prefix)
|
||||
assert_equal("/", namespace.delim)
|
||||
assert_equal({"X-PARAM" => ["FLAG1", "FLAG2"]}, namespace.extensions)
|
||||
end
|
||||
end
|
|
@ -51,7 +51,6 @@ REPOSITORIES = {
|
|||
tmpdir: "ruby/tmpdir",
|
||||
English: "ruby/English",
|
||||
"net-protocol": "ruby/net-protocol",
|
||||
"net-imap": "ruby/net-imap",
|
||||
"net-http": "ruby/net-http",
|
||||
bigdecimal: "ruby/bigdecimal",
|
||||
optparse: "ruby/optparse",
|
||||
|
@ -260,12 +259,6 @@ def sync_default_gems(gem)
|
|||
cp_r("#{upstream}/lib/net/protocol.rb", "lib/net")
|
||||
cp_r("#{upstream}/test/net/protocol", "test/net")
|
||||
cp_r("#{upstream}/net-protocol.gemspec", "lib/net")
|
||||
when "net-imap"
|
||||
rm_rf(%w[lib/net/imap.rb lib/net/imap test/net/imap])
|
||||
cp_r("#{upstream}/lib/net/imap.rb", "lib/net")
|
||||
cp_r("#{upstream}/lib/net/imap", "lib/net")
|
||||
cp_r("#{upstream}/test/net/imap", "test/net")
|
||||
cp_r("#{upstream}/net-imap.gemspec", "lib/net/imap")
|
||||
when "net-http"
|
||||
rm_rf(%w[lib/net/http.rb lib/net/http test/net/http])
|
||||
cp_r("#{upstream}/lib/net/http.rb", "lib/net")
|
||||
|
|
Загрузка…
Ссылка в новой задаче