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