Normally sending emails with rails is a piece of cake. But today, it wasn't.

A client wanted to use their Fusemail account to send user registration confirmations, password resets, and so forth. But it just wouldn't work.

I tried every possible value of every option that you could send to Net::SMTP, with no luck.

So I tried telnetting in to the SMTP server, just to see if it existed:

> telnet smtp.fusemail.net 463
Trying 208.70.131.61...
Connected to smtp.fusemail.net.
Escape character is '^]'.

And that was it. Weird. No SMTP headers. Nothing.

> openssl s_client -crlf -connect smtp.fusemail.net:463
220 smtp-gw55.mailanyone.net MailAnyone extSMTP Wed, 24 Feb 2010 14:43:56 -0600

BINGO. It turns out fusemail uses an old version of TLS called "tls on connect"

With regular TLS, you connect to the SMTP server, send a "STARTTLS" command, and then initialize a SSL connection.

But with tls on connect, you have to connect using SSL.

With that bit of knowledge we can easily patch Net::SMTP to work with fusemail

require "openssl"
require "net/smtp"

Net::SMTP.class_eval do
  private
  def do_start(helodomain, user, secret, authtype)
    raise IOError, 'SMTP session already started' if @started
    raise 'openssl library not installed' unless defined?(OpenSSL)

    if RUBY_VERSION > "1.8.6"
      check_auth_args user, secret if user or secret
    else
      check_auth_args user, secret, authtype if user or secret
    end

    sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }

    # This is the important bit. We just open an SSL socket 
    # to use instead of the regular TCP socket.
    ssl = OpenSSL::SSL::SSLSocket.new(sock)
    ssl.sync_close = true
    ssl.connect
    @socket = Net::InternetMessageIO.new(ssl)
    @socket.read_timeout = 60 #@read_timeout

    check_response(critical { recv_response() })
    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 quit
    begin
      getok('QUIT')
    rescue EOFError
    end
  end
end