summaryrefslogtreecommitdiff
path: root/lib/feed2imap/rubyimap.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/feed2imap/rubyimap.rb')
-rw-r--r--lib/feed2imap/rubyimap.rb154
1 files changed, 139 insertions, 15 deletions
diff --git a/lib/feed2imap/rubyimap.rb b/lib/feed2imap/rubyimap.rb
index 4e43f60..192e50b 100644
--- a/lib/feed2imap/rubyimap.rb
+++ b/lib/feed2imap/rubyimap.rb
@@ -1,3 +1,7 @@
+# File fetched from
+# http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/trunk/lib/net/imap.rb?view=log
+############################################################################
+
#
# = net/imap.rb
#
@@ -16,8 +20,9 @@
require "socket"
require "monitor"
require "digest/md5"
+require "strscan"
begin
-require "openssl"
+ require "openssl"
rescue LoadError
end
@@ -273,8 +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 and
- # Net::IMAP::CramMD5Authenticator for examples.
+ # 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.
@@ -284,8 +291,8 @@ module Net
# Disconnects from the server.
def disconnect
- if @usessl
- @sock.to_io.shutdown
+ if SSL::SSLSocket === @sock
+ @sock.io.shutdown
else
@sock.shutdown
end
@@ -326,6 +333,24 @@ module Net
send_command("LOGOUT")
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
+ 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)
+ end
+ @sock.sync_close = true
+ @sock.connect
+ end
+ end
+ end
+
# Sends an AUTHENTICATE command to authenticate the client.
# The +auth_type+ parameter is a string that represents
# the authentication mechanism to be used. Currently Net::IMAP
@@ -1210,7 +1235,7 @@ module Net
class RawData # :nodoc:
def send_data(imap)
- imap.send(:put_string, @data)
+ imap.send!(:put_string, @data)
end
private
@@ -1222,7 +1247,7 @@ module Net
class Atom # :nodoc:
def send_data(imap)
- imap.send(:put_string, @data)
+ imap.send!(:put_string, @data)
end
private
@@ -1234,7 +1259,7 @@ module Net
class QuotedString # :nodoc:
def send_data(imap)
- imap.send(:send_quoted_string, @data)
+ imap.send!(:send_quoted_string, @data)
end
private
@@ -1246,7 +1271,7 @@ module Net
class Literal # :nodoc:
def send_data(imap)
- imap.send(:send_literal, @data)
+ imap.send!(:send_literal, @data)
end
private
@@ -1258,7 +1283,7 @@ module Net
class MessageSet # :nodoc:
def send_data(imap)
- imap.send(:put_string, format_internal(@data))
+ imap.send!(:put_string, format_internal(@data))
end
private
@@ -1768,7 +1793,7 @@ module Net
T_TEXT = :TEXT
BEG_REGEXP = /\G(?:\
-(?# 1: SPACE )( )|\
+(?# 1: SPACE )( +)|\
(?# 2: NIL )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\
(?# 3: NUMBER )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\
(?# 4: ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+]+)|\
@@ -2620,7 +2645,7 @@ module Net
token = match(T_ATOM)
name = token.value.upcase
case name
- when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE)\z/n
+ when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n
result = ResponseCode.new(name, nil)
when /\A(?:PERMANENTFLAGS)\z/n
match(T_SPACE)
@@ -2927,7 +2952,7 @@ module Net
elsif $7
return Token.new(T_RPAR, $+)
else
- parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid")
+ parse_error("[Net::IMAP BUG] DATA_REGEXP is invalid")
end
else
@str.index(/\S*/n, @pos)
@@ -2981,7 +3006,7 @@ module Net
$stderr.printf("@str: %s\n", @str.dump)
$stderr.printf("@pos: %d\n", @pos)
$stderr.printf("@lex_state: %s\n", @lex_state)
- if @token.symbol
+ if @token
$stderr.printf("@token.symbol: %s\n", @token.symbol)
$stderr.printf("@token.value: %s\n", @token.value.inspect)
end
@@ -3066,6 +3091,106 @@ module Net
end
add_authenticator "CRAM-MD5", CramMD5Authenticator
+ # Authenticator for the "DIGEST-MD5" authentication type. See
+ # #authenticate().
+ class 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 { |k| qdval(k.to_s, response[k]) }.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 reponses needs 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
+ end
+ add_authenticator "DIGEST-MD5", DigestMD5Authenticator
+
# Superclass of IMAP errors.
class Error < StandardError
end
@@ -3251,4 +3376,3 @@ EOF
imap.disconnect
end
end
-