【问题标题】:Ruby Mail, how to achieve SSL emailRuby Mail,如何实现 SSL 邮件
【发布时间】:2010-11-30 18:44:28
【问题描述】:

我已通过此脚本成功地使用端口 25(非安全)将电子邮件发送到远程服务器:

require 'rubygems'
require 'mail'

options = { :address              => "mail.domain.com",
            :port                 => 25,
            :domain               => 'mail.domain.com',
            :user_name            => 'somedude@domain.com',
            :password             => 'topsecret',
            :authentication       => 'login',
            :enable_starttls_auto => true  }
 Mail.defaults do
  delivery_method :smtp, options
end

 mail = Mail.new do
      from 'someotherdude@otherdomain.com'
        to 'somedude@domain.com'
   subject 'This is a test email'
      body File.read('body.txt')
 end

puts mail.to_s
mail.deliver!

我现在需要做的是使用他们的 SSL 端口 466。当我尝试它时,我会得到详细说明消息的正常输出,然后它会暂停大约 2 分钟并咳出这个:

/usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/timeout.rb:60:in `rbuf_fill': execution expired (Timeout::Error)
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/protocol.rb:134:in `rbuf_fill'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/protocol.rb:126:in `readline'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/smtp.rb:911:in `recv_response'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/smtp.rb:554:in `do_start'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/smtp.rb:921:in `critical'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/smtp.rb:554:in `do_start'
        from /usr/local/rvm/rubies/ruby-1.8.7-p249/lib/ruby/1.8/net/smtp.rb:525:in `start'
        from /usr/local/rvm/gems/ruby-1.8.7-p249/gems/mail-2.2.10/lib/mail/network/delivery_methods/smtp.rb:127:in `deliver!'
        from /usr/local/rvm/gems/ruby-1.8.7-p249/gems/mail-2.2.10/lib/mail/message.rb:243:in `deliver!'
        from testmail.rb:30

我认为这是因为它甚至无法启动 SSL 身份验证过程。我该怎么做?

【问题讨论】:

    标签: ruby email


    【解决方案1】:

    嗯,阅读 network/delivery_methods/smtp.rb,它看起来不支持 Direct SSL。 TLS 不一样,因为连接从纯文本开始,然后在 starttls 命令上切换到 SSL。你可以在端口 587 上使用 starttls 吗?

    拉起我的评论。

    How to send mail with ruby over smtp with ssl (not with rails, no TLS for gmail)

    这表明您可以通过猴子修补 Net::SMTP 来做到这一点..

    好吧,有点发现问题并可以解决它,但到目前为止,这个解决方案很糟糕......但它确实有效:)

    #!/usr/bin/env ruby
    
    require 'rubygems'
    require "openssl"
    require "net/smtp"
    require "mail"
    
    Net::SMTP.class_eval do
    
      def self.start( address, port = nil,
                      helo = 'localhost.localdomain',
                      user = nil, secret = nil, authtype = nil, use_tls = false,
                      use_ssl = true, &block) # :yield: smtp
        new(address, port).start(helo, user, secret, authtype, use_tls, use_ssl, &block)
      end
    
      def start( helo = 'localhost.localdomain',
                 user = nil, secret = nil, authtype = nil, use_tls = false, use_ssl = true ) # :yield: smtp
        start_method = use_tls ? :do_tls_start : use_ssl ? :do_ssl_start : :do_start
        if block_given?
          begin
            send start_method, helo, user, secret, authtype
            return yield(self)
          ensure
            do_finish
          end
        else
          send start_method, helo, user, secret, authtype
          return self
        end
      end
    
      private
    
      def do_tls_start(helodomain, user, secret, authtype)
        raise IOError, 'SMTP session already started' if @started
    
        check_auth_args user, secret
    
        sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
        @socket = Net::InternetMessageIO.new(sock)
        @socket.read_timeout = 60 #@read_timeout
        @socket.debug_output = STDERR #@debug_output
    
        check_response(critical { recv_response() })
        do_helo(helodomain)
    
        raise 'openssl library not installed' unless defined?(OpenSSL)
        starttls
        ssl = OpenSSL::SSL::SSLSocket.new(sock)
        ssl.sync_close = true
        ssl.connect
        @socket = Net::InternetMessageIO.new(ssl)
        @socket.read_timeout = 60 #@read_timeout
        @socket.debug_output = STDERR #@debug_output
        do_helo(helodomain)
    
        authenticate user, secret, authtype if user
        @started = true
      ensure
        unless @started
          # authentication failed, cancel connection.
            @socket.close if not @started and @socket and not @socket.closed?
          @socket = nil
        end
      end
    
      def do_ssl_start(helodomain, user, secret, authtype)
        raise IOError, 'SMTP session already started' if @started
    
        check_auth_args user, secret
    
        sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
        raise 'openssl library not installed' unless defined?(OpenSSL)
        ssl = OpenSSL::SSL::SSLSocket.new(sock)
        ssl.sync_close = true
        ssl.connect
        @socket = Net::InternetMessageIO.new(ssl)
        @socket.read_timeout = 60 #@read_timeout
        @socket.debug_output = STDERR #@debug_output
    
        check_response(critical { recv_response() })
        do_helo(helodomain)
    
        do_helo(helodomain)
    
        authenticate user, secret, authtype if user
        @started = true
      ensure
        unless @started
          # authentication failed, cancel connection.
            @socket.close if not @started and @socket and not @socket.closed?
          @socket = nil
        end
      end
    
      def do_helo(helodomain)
         begin
          if @esmtp
            ehlo helodomain
          else
            helo helodomain
          end
        rescue Net::ProtocolError
          if @esmtp
            @esmtp = false
            @error_occured = false
            retry
          end
          raise
        end
      end
    
      def starttls
        getok('STARTTLS')
      end
    
      def quit
        begin
          getok('QUIT')
        rescue EOFError, OpenSSL::SSL::SSLError
        end
      end
    end
    
    options = {
      :address              => "mail.domain.net",
      :port                 => 466,
      :domain               => 'mail.domain.net',
      :user_name            => 'doon@domain.net',
      :password             => 'Secret!',
      :authentication       => 'login',
      :use_ssl => true  }
    
    Mail.defaults do
      delivery_method :smtp, options
    end
    
    mail = Mail.new do
      from 'doon@domain.net'
      to 'doon@someotherdomain.com'
      subject 'This is a test email'
      body File.read('body.txt')
    end
    
    puts mail.to_s
    mail.deliver!
    

    由于某种原因,orig 猴子补丁中的 use_ssl 没有加入,并且与 Net::SMTP 中未定义的 VERSION 结合在一起。所以我改变了它,并强制 use_ssl 为真,并且能够发送电子邮件..

    【讨论】:

    • 我们无法控制 466 端口分配,并且端口 587 不可用
    • 嗯,似乎 Net::SMTP 不支持邮件使用的 SSL 开箱即用。看看这里。 stackoverflow.com/questions/708858/… 看起来你可以用猴子补丁 Net::SMTP 来工作..
    • 我试过了,不幸的是它产生了完全相同的结果。
    • 嗯。那么我不确定......也许你可以在本地代理它。这适用于所有出站电子邮件吗?如果是这样,他们可能会在本地运行邮件服务器,然后通过 ssl 将其设置为智能主机?
    • 也许我错误地处理了猴子补丁。你能准确地告诉我它的去向吗?我在 require 'mail' 之后粘贴了它
    【解决方案2】:

    您的意思是使用端口 465 吗?这是标准的备用端口,而不是 466。您可能会超时连接到错误的端口。

    【讨论】:

    • 不,端口是由我们的服务提供商分配的。其他人实际上也建议过,所以我们也尝试过,但无济于事。
    • 当您“telnet mail.myserver.com 466”时会发生什么?你有联系吗?
    • 没有。也试过465。只是说“正在连接到 mail.safetysend.com...”
    • 听起来你的服务器有问题。与系统管理员交谈。
    • 与上述 Doon 的解决方案配合良好。
    猜你喜欢
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    • 2014-11-28
    • 2020-07-26
    • 1970-01-01
    • 2013-07-27
    相关资源
    最近更新 更多