From acbb1c943457d3b8802f099ee0e34331bc78bd38 Mon Sep 17 00:00:00 2001 From: aamine Date: Tue, 25 Apr 2000 09:23:21 +0000 Subject: [PATCH] version 1.1.16 o smtp.rb: SMTP AUTH (contributed by Kazuhiro Izawa) git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@677 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/net/protocol.rb | 3 ++- lib/net/smtp.rb | 60 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index d8e358b95f..81e8e987eb 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -15,7 +15,7 @@ require 'socket' module Net - Version = '1.1.15' + Version = '1.1.16' =begin @@ -346,6 +346,7 @@ Object SyntaxErrorCode = ErrorCode.mkchild( ProtoSyntaxError ) FatalErrorCode = ErrorCode.mkchild( ProtoFatalError ) ServerErrorCode = ErrorCode.mkchild( ProtoServerError ) + AuthErrorCode = ErrorCode.mkchild( ProtoAuthError ) RetriableCode = ReplyCode.mkchild( ProtoRetriableError ) UnknownCode = ReplyCode.mkchild( ProtoUnknownError ) diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index 271441279a..fe3dd9e1b2 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -11,6 +11,7 @@ You can freely distribute/modify this library. require 'net/protocol' +require 'md5' module Net @@ -32,10 +33,13 @@ Net::Protocol === Methods -: start( helo_domain = ENV['HOSTNAME'] ) +: start( helo_domain = ENV['HOSTNAME'] || ENV['HOST'], account = nil, password = nil, authtype = nil ) This method opens TCP connection and start SMTP. If protocol had been started, do nothing and return false. + If account and password are given, is trying to get authentication + by using AUTH command. "authtype" is :plain (symbol) or :cram_md5. + : sendmail( mailsrc, from_addr, to_addrs ) This method sends 'mailsrc' as mail. SMTPSession read strings from 'mailsrc' by calling 'each' iterator, and convert them @@ -102,12 +106,15 @@ Net::Protocol @command.data end - def do_start( helodom = ENV['HOSTNAME'] ) + def do_start( helodom = nil, + user = nil, secret = nil, authtype = nil ) unless helodom then - raise ArgumentError, "cannot get hostname" + helodom = ENV['HOSTNAME'] || ENV['HOST'] + unless helodom then + raise ArgumentError, "cannot get hostname" + end end - @esmtp = false begin if @esmtp then @command.ehlo helodom @@ -122,6 +129,15 @@ Net::Protocol raise end end + + if user and secret then + begin + mid = 'auth_' + (authtype || 'cram_md5').to_s + @command.send mid, user, secret + rescue NameError + raise ArgumentError, "wrong auth type #{authtype.to_s}" + end + end end end @@ -154,6 +170,35 @@ Net::Protocol end + # "PLAIN" authentication [RFC2554] + def auth_plain( user, secret ) + critical { + getok sprintf( 'AUTH PLAIN %s', + ["\0#{user}\0#{secret}"].pack('m').chomp ) + } + end + + # "CRAM-MD5" authentication [RFC2195] + def auth_cram_md5( user, secret ) + critical { + rep = getok( 'AUTH CRAM-MD5', ContinueCode ) + challenge = rep.msg.split(' ')[1].unpack('m')[0] + secret = MD5.new( secret ).digest if secret.size > 64 + + isecret = secret + "\0" * (64 - secret.size) + osecret = isecret.dup + 0.upto( 63 ) do |i| + isecret[i] ^= 0x36 + osecret[i] ^= 0x5c + end + tmp = MD5.new( isecret + challenge ).digest + tmp = MD5.new( osecret + tmp ).hexdigest + + getok [user + ' ' + tmp].pack('m').chomp + } + end + + def mailfrom( fromaddr ) critical { getok sprintf( 'MAIL FROM:<%s>', fromaddr ) @@ -198,7 +243,6 @@ Net::Protocol arr = read_reply stat = arr[0][0,3] - klass = UnknownCode klass = case stat[0] when ?2 then SuccessCode when ?3 then ContinueCode @@ -206,9 +250,11 @@ Net::Protocol when ?5 then case stat[1] when ?0 then SyntaxErrorCode + when ?3 then AuthErrorCode when ?5 then FatalErrorCode end end + klass ||= UnknownCode Response.new( klass, stat, arr.join('') ) end @@ -217,7 +263,9 @@ Net::Protocol def read_reply arr = [] - while (str = @socket.readline)[3] == ?- do # ex: "210-..." + while true do + str = @socket.readline + break unless str[3] == ?- # ex: "210-..." arr.push str end arr.push str