2003-08-06 07:37:55 -04:00
|
|
|
# = net/smtp.rb
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2006-08-14 22:21:59 -04:00
|
|
|
# Copyright (c) 1999-2006 Yukihiro Matsumoto.
|
2004-03-06 12:08:21 -05:00
|
|
|
#
|
2006-08-14 22:21:59 -04:00
|
|
|
# Copyright (c) 1999-2006 Minero Aoki.
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# Written & maintained by Minero Aoki <aamine@loveruby.net>.
|
|
|
|
#
|
|
|
|
# Documented by William Webber and Minero Aoki.
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# This program is free software. You can re-distribute and/or
|
2004-03-06 12:08:21 -05:00
|
|
|
# modify this program under the same terms as Ruby itself.
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
2004-03-06 12:08:21 -05:00
|
|
|
# NOTE: You can find Japanese version of this document at:
|
|
|
|
# http://www.ruby-lang.org/ja/man/index.cgi?cmd=view;name=net%2Fsmtp.rb
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# $Id$
|
2004-04-21 05:40:48 -04:00
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# See Net::SMTP for documentation.
|
2004-04-21 05:40:48 -04:00
|
|
|
#
|
1999-10-13 03:29:15 -04:00
|
|
|
|
2000-06-12 12:42:46 -04:00
|
|
|
require 'net/protocol'
|
2001-12-01 09:07:01 -05:00
|
|
|
require 'digest/md5'
|
2004-03-06 12:08:21 -05:00
|
|
|
require 'timeout'
|
2004-04-21 05:40:48 -04:00
|
|
|
begin
|
2006-08-14 22:21:59 -04:00
|
|
|
require 'openssl'
|
2004-04-21 05:40:48 -04:00
|
|
|
rescue LoadError
|
|
|
|
end
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
module Net
|
2000-06-12 12:42:46 -04:00
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# Module mixed in to all SMTP error classes
|
2003-07-01 22:34:39 -04:00
|
|
|
module SMTPError
|
|
|
|
# This *class* is module for some reason.
|
|
|
|
# In ruby 1.9.x, this module becomes a class.
|
|
|
|
end
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# Represents an SMTP authentication error.
|
2003-07-01 22:34:39 -04:00
|
|
|
class SMTPAuthenticationError < ProtoAuthError
|
|
|
|
include SMTPError
|
|
|
|
end
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# Represents SMTP error code 420 or 450, a temporary error.
|
2003-07-01 22:34:39 -04:00
|
|
|
class SMTPServerBusy < ProtoServerError
|
|
|
|
include SMTPError
|
|
|
|
end
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# Represents an SMTP command syntax error (error code 500)
|
2003-07-01 22:34:39 -04:00
|
|
|
class SMTPSyntaxError < ProtoSyntaxError
|
|
|
|
include SMTPError
|
|
|
|
end
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# Represents a fatal SMTP error (error code 5xx, except for 500)
|
2003-07-01 22:34:39 -04:00
|
|
|
class SMTPFatalError < ProtoFatalError
|
|
|
|
include SMTPError
|
|
|
|
end
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# Unexpected reply code returned from server.
|
2003-07-01 22:34:39 -04:00
|
|
|
class SMTPUnknownError < ProtoUnknownError
|
|
|
|
include SMTPError
|
|
|
|
end
|
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# = Net::SMTP
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# == What is This Library?
|
|
|
|
#
|
|
|
|
# This library provides functionality to send internet
|
|
|
|
# mail via SMTP, the Simple Mail Transfer Protocol. For details of
|
|
|
|
# SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt).
|
|
|
|
#
|
|
|
|
# == What is This Library NOT?
|
|
|
|
#
|
|
|
|
# This library does NOT provide functions to compose internet mails.
|
|
|
|
# You must create them by yourself. If you want better mail support,
|
|
|
|
# try RubyMail or TMail. You can get both libraries from RAA.
|
|
|
|
# (http://www.ruby-lang.org/en/raa.html)
|
|
|
|
#
|
|
|
|
# FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt).
|
|
|
|
#
|
|
|
|
# == Examples
|
|
|
|
#
|
|
|
|
# === Sending Messages
|
|
|
|
#
|
|
|
|
# You must open a connection to an SMTP server before sending messages.
|
|
|
|
# The first argument is the address of your SMTP server, and the second
|
|
|
|
# argument is the port number. Using SMTP.start with a block is the simplest
|
|
|
|
# way to do this. This way, the SMTP connection is closed automatically
|
|
|
|
# after the block is executed.
|
|
|
|
#
|
|
|
|
# require 'net/smtp'
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
|
|
|
|
# # Use the SMTP object smtp only in this block.
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# Replace 'your.smtp.server' with your SMTP server. Normally
|
|
|
|
# your system manager or internet provider supplies a server
|
|
|
|
# for you.
|
|
|
|
#
|
|
|
|
# Then you can send messages.
|
|
|
|
#
|
|
|
|
# msgstr = <<END_OF_MESSAGE
|
|
|
|
# From: Your Name <your@mail.address>
|
|
|
|
# To: Destination Address <someone@example.com>
|
|
|
|
# Subject: test message
|
|
|
|
# Date: Sat, 23 Jun 2001 16:26:43 +0900
|
|
|
|
# Message-Id: <unique.message.id.string@example.com>
|
|
|
|
#
|
|
|
|
# This is a test message.
|
|
|
|
# END_OF_MESSAGE
|
|
|
|
#
|
|
|
|
# require 'net/smtp'
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
|
|
|
|
# smtp.send_message msgstr,
|
|
|
|
# 'your@mail.address',
|
|
|
|
# 'his_addess@example.com'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# === Closing the Session
|
|
|
|
#
|
|
|
|
# You MUST close the SMTP session after sending messages, by calling
|
|
|
|
# the #finish method:
|
|
|
|
#
|
|
|
|
# # using SMTP#finish
|
|
|
|
# smtp = Net::SMTP.start('your.smtp.server', 25)
|
|
|
|
# smtp.send_message msgstr, 'from@address', 'to@address'
|
|
|
|
# smtp.finish
|
|
|
|
#
|
|
|
|
# You can also use the block form of SMTP.start/SMTP#start. This closes
|
|
|
|
# the SMTP session automatically:
|
|
|
|
#
|
|
|
|
# # using block form of SMTP.start
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
|
|
|
|
# smtp.send_message msgstr, 'from@address', 'to@address'
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# I strongly recommend this scheme. This form is simpler and more robust.
|
|
|
|
#
|
|
|
|
# === HELO domain
|
|
|
|
#
|
|
|
|
# In almost all situations, you must provide a third argument
|
|
|
|
# to SMTP.start/SMTP#start. This is the domain name which you are on
|
|
|
|
# (the host to send mail from). It is called the "HELO domain".
|
|
|
|
# The SMTP server will judge whether it should send or reject
|
|
|
|
# the SMTP session by inspecting the HELO domain.
|
|
|
|
#
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25,
|
|
|
|
# 'mail.from.domain') { |smtp| ... }
|
|
|
|
#
|
|
|
|
# === SMTP Authentication
|
|
|
|
#
|
|
|
|
# The Net::SMTP class supports three authentication schemes;
|
|
|
|
# PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
|
|
|
|
# To use SMTP authentication, pass extra arguments to
|
|
|
|
# SMTP.start/SMTP#start.
|
|
|
|
#
|
|
|
|
# # PLAIN
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
|
|
|
|
# 'Your Account', 'Your Password', :plain)
|
|
|
|
# # LOGIN
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
|
|
|
|
# 'Your Account', 'Your Password', :login)
|
|
|
|
#
|
|
|
|
# # CRAM MD5
|
|
|
|
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
|
|
|
|
# 'Your Account', 'Your Password', :cram_md5)
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
2003-07-01 22:34:39 -04:00
|
|
|
class SMTP
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2003-05-02 10:35:01 -04:00
|
|
|
Revision = %q$Revision$.split[1]
|
2004-05-09 10:42:43 -04:00
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# The default SMTP port, port 25.
|
2003-05-02 10:35:01 -04:00
|
|
|
def SMTP.default_port
|
|
|
|
25
|
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# The default SMTP/SSL port, port 465.
|
|
|
|
def SMTP.default_ssl_port
|
|
|
|
465
|
|
|
|
end
|
|
|
|
|
|
|
|
# The default SMTP/TLS (STARTTLS) port, port 587.
|
|
|
|
def SMTP.default_tls_port
|
|
|
|
587
|
|
|
|
end
|
|
|
|
|
|
|
|
@ssl = false
|
|
|
|
@tls = false
|
|
|
|
@ssl_context = nil
|
|
|
|
|
|
|
|
# Enables SMTP/SSL for all new objects.
|
|
|
|
# +context+ is a OpenSSL::SSL::SSLContext object.
|
|
|
|
def SMTP.enable_ssl(context = SMTP.default_ssl_context)
|
|
|
|
raise 'openssl library not installed' unless defined?(OpenSSL)
|
|
|
|
raise ArgumentError, "SSL and TLS is exclusive" if @tls
|
|
|
|
@ssl = true
|
|
|
|
@ssl_context = context
|
|
|
|
end
|
|
|
|
|
|
|
|
# Disables SMTP/SSL for all new objects.
|
|
|
|
def SMTP.disable_ssl
|
|
|
|
@ssl = false
|
|
|
|
@ssl_context = nil
|
|
|
|
end
|
2004-04-21 05:40:48 -04:00
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# true if new objects use SMTP/SSL.
|
|
|
|
def SMTP.use_ssl?
|
|
|
|
@ssl
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# Enables SMTP/SSL for all new objects.
|
|
|
|
# +context+ is a OpenSSL::SSL::Context object.
|
|
|
|
def SMTP.enable_tls(context = SMTP.default_ssl_context)
|
|
|
|
raise 'openssl library not installed' unless defined?(OpenSSL)
|
|
|
|
raise ArgumentError, "SSL and TLS is exclusive" if @ssl
|
|
|
|
@tls = false
|
|
|
|
@ssl_context = context
|
|
|
|
end
|
|
|
|
|
|
|
|
# Disable SMTP/TLS for all new objects.
|
2004-04-21 05:40:48 -04:00
|
|
|
def SMTP.disable_tls
|
2006-08-14 22:21:59 -04:00
|
|
|
@tls = false
|
|
|
|
@ssl_context = nil
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# true if new objects use SMTP/TLS.
|
2004-04-21 05:40:48 -04:00
|
|
|
def SMTP.use_tls?
|
2006-08-14 22:21:59 -04:00
|
|
|
@tls
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def SMTP.ssl_context
|
|
|
|
@ssl_context
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def SMTP.default_ssl_context
|
|
|
|
ctx = OpenSSL::SSL::SSLContext.new
|
|
|
|
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
|
|
ctx
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
|
|
|
# Creates a new Net::SMTP object.
|
|
|
|
#
|
|
|
|
# +address+ is the hostname or ip address of your SMTP
|
|
|
|
# server. +port+ is the port to connect to; it defaults to
|
|
|
|
# port 25.
|
|
|
|
#
|
|
|
|
# This method does not open the TCP connection. You can use
|
|
|
|
# SMTP.start instead of SMTP.new if you want to do everything
|
|
|
|
# at once. Otherwise, follow SMTP.new with SMTP#start.
|
|
|
|
#
|
2004-04-21 05:23:56 -04:00
|
|
|
def initialize(address, port = nil)
|
2003-05-02 10:35:01 -04:00
|
|
|
@address = address
|
2003-07-01 22:34:39 -04:00
|
|
|
@port = (port || SMTP.default_port)
|
2000-04-18 05:39:02 -04:00
|
|
|
@esmtp = true
|
2003-05-02 10:35:01 -04:00
|
|
|
@socket = nil
|
|
|
|
@started = false
|
|
|
|
@open_timeout = 30
|
|
|
|
@read_timeout = 60
|
2003-07-01 22:34:39 -04:00
|
|
|
@error_occured = false
|
2003-05-02 10:35:01 -04:00
|
|
|
@debug_output = nil
|
2006-08-14 22:21:59 -04:00
|
|
|
if SMTP.use_ssl? or SMTP.use_tls?
|
|
|
|
@ssl = true
|
|
|
|
if SMTP.use_ssl?
|
|
|
|
@ssl_mode = :ssl
|
|
|
|
else
|
|
|
|
@ssl_mode = :tls
|
|
|
|
end
|
|
|
|
@certs = SMTP.certs
|
|
|
|
@verify = SMTP.verify
|
|
|
|
end
|
2003-05-02 10:35:01 -04:00
|
|
|
end
|
2004-04-21 05:40:48 -04:00
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# Provide human-readable stringification of class state.
|
2003-05-02 10:35:01 -04:00
|
|
|
def inspect
|
2003-07-01 22:34:39 -04:00
|
|
|
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
|
2000-04-18 05:39:02 -04:00
|
|
|
end
|
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# +true+ if the SMTP object uses ESMTP (which it does by default).
|
2002-02-19 07:33:52 -05:00
|
|
|
def esmtp?
|
|
|
|
@esmtp
|
|
|
|
end
|
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# Set whether to use ESMTP or not. This should be done before
|
|
|
|
# calling #start. Note that if #start is called in ESMTP mode,
|
|
|
|
# and the connection fails due to a ProtocolError, the SMTP
|
|
|
|
# object will automatically switch to plain SMTP mode and
|
|
|
|
# retry (but not vice versa).
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2004-04-21 05:23:56 -04:00
|
|
|
def esmtp=(bool)
|
2002-02-19 07:33:52 -05:00
|
|
|
@esmtp = bool
|
|
|
|
end
|
|
|
|
|
|
|
|
alias esmtp esmtp?
|
2000-04-18 05:39:02 -04:00
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# true if this object uses SMTP/SSL.
|
|
|
|
def use_ssl?
|
|
|
|
@ssl
|
|
|
|
end
|
|
|
|
|
|
|
|
# true if this object uses SMTP/TLS
|
2004-04-21 05:40:48 -04:00
|
|
|
def use_tls?
|
2006-08-14 22:21:59 -04:00
|
|
|
@tls
|
|
|
|
end
|
|
|
|
|
|
|
|
# Enables SMTP/SSL for this object. Must be called before the
|
|
|
|
# connection is established to have any effect.
|
|
|
|
# +context+ is a OpenSSL::SSL::SSLContext object.
|
|
|
|
def enable_ssl(context = SMTP.default_ssl_context)
|
|
|
|
raise 'openssl library not installed' unless defined?(OpenSSL)
|
|
|
|
raise ArgumentError, "SSL and TLS is exclusive" if @tls
|
|
|
|
@ssl = true
|
|
|
|
@ssl_context = context
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# Disables SMTP/SSL for this object. Must be called before the
|
|
|
|
# connection is established to have any effect.
|
|
|
|
def disable_ssl
|
|
|
|
@ssl = false
|
|
|
|
@ssl_context = nil
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# Enables SMTP/TLS (STARTTLS) for this object.
|
|
|
|
# +context+ is a OpenSSL::SSL::SSLContext object.
|
|
|
|
def enable_tls(context = SMTP.default_ssl_context)
|
|
|
|
raise 'openssl library not installed' unless defined?(OpenSSL)
|
|
|
|
raise ArgumentError, "SSL and TLS is exclusive" if @ssl
|
|
|
|
@tls = true
|
|
|
|
@ssl_context = context
|
|
|
|
end
|
|
|
|
|
|
|
|
# Disables SMTP/TLS (STARTTLS) for this object. Must be called
|
|
|
|
# before the connection is established to have any effect.
|
2004-04-21 05:40:48 -04:00
|
|
|
def disable_tls
|
2006-08-14 22:21:59 -04:00
|
|
|
@ssl = false
|
|
|
|
@ssl_context = nil
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# The address of the SMTP server to connect to.
|
2003-05-02 10:35:01 -04:00
|
|
|
attr_reader :address
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# The port number of the SMTP server to connect to.
|
2003-05-02 10:35:01 -04:00
|
|
|
attr_reader :port
|
1999-09-22 03:32:33 -04:00
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# Seconds to wait while attempting to open a connection.
|
|
|
|
# If the connection cannot be opened within this time, a
|
|
|
|
# TimeoutError is raised.
|
2003-05-02 10:35:01 -04:00
|
|
|
attr_accessor :open_timeout
|
2003-08-06 07:37:55 -04:00
|
|
|
|
|
|
|
# Seconds to wait while reading one block (by one read(2) call).
|
|
|
|
# If the read(2) call does not complete within this time, a
|
|
|
|
# TimeoutError is raised.
|
2003-05-02 10:35:01 -04:00
|
|
|
attr_reader :read_timeout
|
2000-02-21 10:25:37 -05:00
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# Set the number of seconds to wait until timing-out a read(2)
|
|
|
|
# call.
|
2004-04-21 05:23:56 -04:00
|
|
|
def read_timeout=(sec)
|
2003-05-02 10:35:01 -04:00
|
|
|
@socket.read_timeout = sec if @socket
|
|
|
|
@read_timeout = sec
|
|
|
|
end
|
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# WARNING: This method causes serious security holes.
|
|
|
|
# Use this method for only debugging.
|
|
|
|
#
|
|
|
|
# Set an output stream for debug logging.
|
|
|
|
# You must call this before #start.
|
|
|
|
#
|
|
|
|
# # example
|
|
|
|
# smtp = Net::SMTP.new(addr, port)
|
|
|
|
# smtp.set_debug_output $stderr
|
2004-05-09 10:42:43 -04:00
|
|
|
# smtp.start do |smtp|
|
2003-08-06 07:37:55 -04:00
|
|
|
# ....
|
2004-05-09 10:42:43 -04:00
|
|
|
# end
|
|
|
|
#
|
2006-08-14 22:21:59 -04:00
|
|
|
def debug_output=(arg)
|
2003-05-02 10:35:01 -04:00
|
|
|
@debug_output = arg
|
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
alias set_debug_output debug_output=
|
|
|
|
|
2003-05-02 10:35:01 -04:00
|
|
|
#
|
|
|
|
# SMTP session control
|
|
|
|
#
|
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# Creates a new Net::SMTP object and connects to the server.
|
|
|
|
#
|
|
|
|
# This method is equivalent to:
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# Net::SMTP.new(address, port).start(helo_domain, account, password, authtype)
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Example
|
|
|
|
#
|
|
|
|
# Net::SMTP.start('your.smtp.server') do |smtp|
|
|
|
|
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# === Block Usage
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# If called with a block, the newly-opened Net::SMTP object is yielded
|
|
|
|
# to the block, and automatically closed when the block finishes. If called
|
|
|
|
# without a block, the newly-opened Net::SMTP object is returned to
|
|
|
|
# the caller, and it is the caller's responsibility to close it when
|
|
|
|
# finished.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Parameters
|
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +address+ is the hostname or ip address of your smtp server.
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +port+ is the port to connect to; it defaults to port 25.
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +helo+ is the _HELO_ _domain_ provided by the client to the
|
|
|
|
# server (see overview comments); it defaults to 'localhost.localdomain'.
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# The remaining arguments are used for SMTP authentication, if required
|
|
|
|
# or desired. +user+ is the account name; +secret+ is your password
|
|
|
|
# or other authentication token; and +authtype+ is the authentication
|
|
|
|
# type, one of :plain, :login, or :cram_md5. See the discussion of
|
|
|
|
# SMTP Authentication in the overview notes.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Errors
|
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# This method may raise:
|
|
|
|
#
|
|
|
|
# * Net::SMTPAuthenticationError
|
|
|
|
# * Net::SMTPServerBusy
|
|
|
|
# * Net::SMTPSyntaxError
|
|
|
|
# * Net::SMTPFatalError
|
|
|
|
# * Net::SMTPUnknownError
|
|
|
|
# * IOError
|
|
|
|
# * TimeoutError
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2004-04-21 05:23:56 -04:00
|
|
|
def SMTP.start(address, port = nil, helo = 'localhost.localdomain',
|
|
|
|
user = nil, secret = nil, authtype = nil,
|
|
|
|
&block) # :yield: smtp
|
2003-05-02 10:35:01 -04:00
|
|
|
new(address, port).start(helo, user, secret, authtype, &block)
|
|
|
|
end
|
|
|
|
|
2003-08-06 07:37:55 -04:00
|
|
|
# +true+ if the SMTP session has been started.
|
2003-05-02 10:35:01 -04:00
|
|
|
def started?
|
|
|
|
@started
|
|
|
|
end
|
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# Opens a TCP connection and starts the SMTP session.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Parameters
|
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see
|
|
|
|
# the discussion in the overview notes.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# If both of +user+ and +secret+ are given, SMTP authentication
|
|
|
|
# will be attempted using the AUTH command. +authtype+ specifies
|
|
|
|
# the type of authentication to attempt; it must be one of
|
|
|
|
# :login, :plain, and :cram_md5. See the notes on SMTP Authentication
|
|
|
|
# in the overview.
|
|
|
|
#
|
|
|
|
# === Block Usage
|
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# When this methods is called with a block, the newly-started SMTP
|
|
|
|
# object is yielded to the block, and automatically closed after
|
|
|
|
# the block call finishes. Otherwise, it is the caller's
|
|
|
|
# responsibility to close the session when finished.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Example
|
|
|
|
#
|
|
|
|
# This is very similar to the class method SMTP.start.
|
|
|
|
#
|
|
|
|
# require 'net/smtp'
|
|
|
|
# smtp = Net::SMTP.new('smtp.mail.server', 25)
|
|
|
|
# smtp.start(helo_domain, account, password, authtype) do |smtp|
|
|
|
|
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# The primary use of this method (as opposed to SMTP.start)
|
|
|
|
# is probably to set debugging (#set_debug_output) or ESMTP
|
|
|
|
# (#esmtp=), which must be done before the session is
|
|
|
|
# started.
|
|
|
|
#
|
|
|
|
# === Errors
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# If session has already been started, an IOError will be raised.
|
|
|
|
#
|
|
|
|
# This method may raise:
|
|
|
|
#
|
|
|
|
# * Net::SMTPAuthenticationError
|
|
|
|
# * Net::SMTPServerBusy
|
|
|
|
# * Net::SMTPSyntaxError
|
|
|
|
# * Net::SMTPFatalError
|
|
|
|
# * Net::SMTPUnknownError
|
|
|
|
# * IOError
|
|
|
|
# * TimeoutError
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2004-04-21 05:23:56 -04:00
|
|
|
def start(helo = 'localhost.localdomain',
|
2004-05-09 10:42:43 -04:00
|
|
|
user = nil, secret = nil, authtype = nil) # :yield: smtp
|
2003-05-02 10:35:01 -04:00
|
|
|
if block_given?
|
|
|
|
begin
|
2006-08-14 22:21:59 -04:00
|
|
|
do_start helo, user, secret, authtype
|
2003-05-02 10:35:01 -04:00
|
|
|
return yield(self)
|
|
|
|
ensure
|
2003-09-29 14:12:20 -04:00
|
|
|
do_finish
|
2003-05-02 10:35:01 -04:00
|
|
|
end
|
|
|
|
else
|
2006-08-14 22:21:59 -04:00
|
|
|
do_start helo, user, secret, authtype
|
2003-05-02 10:35:01 -04:00
|
|
|
return self
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
# Finishes the SMTP session and closes TCP connection.
|
|
|
|
# Raises IOError if not started.
|
|
|
|
def finish
|
|
|
|
raise IOError, 'not yet started' unless started?
|
|
|
|
do_finish
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def do_start(helo_domain, user, secret, authtype)
|
2003-07-01 22:34:39 -04:00
|
|
|
raise IOError, 'SMTP session already started' if @started
|
2006-08-14 22:21:59 -04:00
|
|
|
if user or secret
|
|
|
|
check_auth_method authtype
|
|
|
|
check_auth_args user, secret
|
|
|
|
end
|
2004-04-21 05:40:48 -04:00
|
|
|
s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
|
2006-08-14 22:21:59 -04:00
|
|
|
logging "Connection opened: #{@address}:#{@port}"
|
|
|
|
if use_ssl?
|
|
|
|
s = new_ssl_socket(s)
|
|
|
|
s.connect
|
|
|
|
logging "SMTP/SSL started"
|
|
|
|
end
|
|
|
|
@socket = new_internet_message_io(s)
|
2003-07-01 22:34:39 -04:00
|
|
|
check_response(critical { recv_response() })
|
2006-08-14 22:21:59 -04:00
|
|
|
do_helo helo_domain
|
|
|
|
if use_tls?
|
|
|
|
s = new_ssl_socket(s)
|
2004-04-21 05:40:48 -04:00
|
|
|
starttls
|
|
|
|
s.connect
|
2006-08-14 22:21:59 -04:00
|
|
|
logging "SMTP/TLS started"
|
|
|
|
@socket = new_internet_message_io(s)
|
2004-04-21 05:40:48 -04:00
|
|
|
# helo response may be different after STARTTLS
|
2006-08-14 22:21:59 -04:00
|
|
|
do_helo helo_domain
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
|
|
|
authenticate user, secret, authtype if user
|
|
|
|
@started = true
|
|
|
|
ensure
|
|
|
|
unless @started
|
|
|
|
# authentication failed, cancel connection.
|
|
|
|
s.close if s and not s.closed?
|
|
|
|
@socket = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def new_internet_message_io(s)
|
|
|
|
io = InternetMessageIO.new(s)
|
|
|
|
io.read_timeout = @read_timeout
|
|
|
|
io.debug_output = @debug_output
|
|
|
|
io
|
|
|
|
end
|
|
|
|
|
|
|
|
def new_ssl_socket(s)
|
|
|
|
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
|
|
|
|
s.sync_close = true
|
|
|
|
s
|
|
|
|
end
|
|
|
|
|
|
|
|
def do_helo(helo_domain)
|
2004-04-21 05:40:48 -04:00
|
|
|
begin
|
2002-11-21 06:50:09 -05:00
|
|
|
if @esmtp
|
2006-08-14 22:21:59 -04:00
|
|
|
ehlo helo_domain
|
2000-04-18 05:39:02 -04:00
|
|
|
else
|
2006-08-14 22:21:59 -04:00
|
|
|
helo helo_domain
|
2000-04-18 05:39:02 -04:00
|
|
|
end
|
2000-02-21 10:25:37 -05:00
|
|
|
rescue ProtocolError
|
2002-11-21 06:50:09 -05:00
|
|
|
if @esmtp
|
2000-04-18 05:39:02 -04:00
|
|
|
@esmtp = false
|
2003-07-01 22:34:39 -04:00
|
|
|
@error_occured = false
|
2000-04-18 05:39:02 -04:00
|
|
|
retry
|
|
|
|
end
|
2003-05-02 10:35:01 -04:00
|
|
|
raise
|
2000-02-21 10:25:37 -05:00
|
|
|
end
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
2003-09-29 14:12:20 -04:00
|
|
|
|
|
|
|
def do_finish
|
2003-07-01 22:34:39 -04:00
|
|
|
quit if @socket and not @socket.closed? and not @error_occured
|
2003-09-29 14:12:20 -04:00
|
|
|
ensure
|
|
|
|
@started = false
|
|
|
|
@error_occured = false
|
2003-05-02 10:35:01 -04:00
|
|
|
@socket.close if @socket and not @socket.closed?
|
|
|
|
@socket = nil
|
2001-12-30 14:18:45 -05:00
|
|
|
end
|
1999-09-22 03:32:33 -04:00
|
|
|
|
2001-12-30 14:18:45 -05:00
|
|
|
#
|
2006-08-14 22:21:59 -04:00
|
|
|
# Message Sending
|
2001-12-30 14:18:45 -05:00
|
|
|
#
|
|
|
|
|
2003-07-01 22:34:39 -04:00
|
|
|
public
|
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# Sends +msgstr+ as a message. Single CR ("\r") and LF ("\n") found
|
|
|
|
# in the +msgstr+, are converted into the CR LF pair. You cannot send a
|
|
|
|
# binary message with this method. +msgstr+ should include both
|
|
|
|
# the message headers and body.
|
|
|
|
#
|
|
|
|
# +from_addr+ is a String representing the source mail address.
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +to_addr+ is a String or Strings or Array of Strings, representing
|
|
|
|
# the destination mail address or addresses.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Example
|
|
|
|
#
|
|
|
|
# Net::SMTP.start('smtp.example.com') do |smtp|
|
2003-08-06 07:37:55 -04:00
|
|
|
# smtp.send_message msgstr,
|
|
|
|
# 'from@example.com',
|
|
|
|
# ['dest@example.com', 'dest2@example.com']
|
2004-05-09 10:42:43 -04:00
|
|
|
# end
|
|
|
|
#
|
|
|
|
# === Errors
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# This method may raise:
|
|
|
|
#
|
|
|
|
# * Net::SMTPServerBusy
|
|
|
|
# * Net::SMTPSyntaxError
|
|
|
|
# * Net::SMTPFatalError
|
|
|
|
# * Net::SMTPUnknownError
|
|
|
|
# * IOError
|
|
|
|
# * TimeoutError
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2004-04-21 05:23:56 -04:00
|
|
|
def send_message(msgstr, from_addr, *to_addrs)
|
2006-08-14 22:21:59 -04:00
|
|
|
raise IOError, 'closed session' unless @socket
|
|
|
|
mailfrom from_addr
|
|
|
|
rcptto_list to_addrs
|
|
|
|
data msgstr
|
2001-12-30 14:18:45 -05:00
|
|
|
end
|
|
|
|
|
2003-07-01 22:34:39 -04:00
|
|
|
alias send_mail send_message
|
|
|
|
alias sendmail send_message # obsolete
|
2001-12-30 14:18:45 -05:00
|
|
|
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# Opens a message writer stream and gives it to the block.
|
|
|
|
# The stream is valid only in the block, and has these methods:
|
|
|
|
#
|
|
|
|
# puts(str = ''):: outputs STR and CR LF.
|
2004-05-09 10:42:43 -04:00
|
|
|
# print(str):: outputs STR.
|
|
|
|
# printf(fmt, *args):: outputs sprintf(fmt,*args).
|
|
|
|
# write(str):: outputs STR and returns the length of written bytes.
|
|
|
|
# <<(str):: outputs STR and returns self.
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# If a single CR ("\r") or LF ("\n") is found in the message,
|
|
|
|
# it is converted to the CR LF pair. You cannot send a binary
|
|
|
|
# message with this method.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Parameters
|
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +from_addr+ is a String representing the source mail address.
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
2003-08-06 07:37:55 -04:00
|
|
|
# +to_addr+ is a String or Strings or Array of Strings, representing
|
|
|
|
# the destination mail address or addresses.
|
|
|
|
#
|
2004-05-09 10:42:43 -04:00
|
|
|
# === Example
|
|
|
|
#
|
|
|
|
# Net::SMTP.start('smtp.example.com', 25) do |smtp|
|
|
|
|
# smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f|
|
2003-08-06 07:37:55 -04:00
|
|
|
# f.puts 'From: from@example.com'
|
|
|
|
# f.puts 'To: dest@example.com'
|
|
|
|
# f.puts 'Subject: test message'
|
|
|
|
# f.puts
|
|
|
|
# f.puts 'This is a test message.'
|
2004-05-09 10:42:43 -04:00
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# === Errors
|
2003-08-06 07:37:55 -04:00
|
|
|
#
|
|
|
|
# This method may raise:
|
|
|
|
#
|
|
|
|
# * Net::SMTPServerBusy
|
|
|
|
# * Net::SMTPSyntaxError
|
|
|
|
# * Net::SMTPFatalError
|
|
|
|
# * Net::SMTPUnknownError
|
|
|
|
# * IOError
|
|
|
|
# * TimeoutError
|
2004-05-09 10:42:43 -04:00
|
|
|
#
|
|
|
|
def open_message_stream(from_addr, *to_addrs, &block) # :yield: stream
|
2003-07-29 05:48:30 -04:00
|
|
|
raise IOError, 'closed session' unless @socket
|
2003-07-01 22:34:39 -04:00
|
|
|
mailfrom from_addr
|
2006-08-14 22:21:59 -04:00
|
|
|
rcptto_list to_addrs
|
|
|
|
data(&block)
|
2003-05-02 10:35:01 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
alias ready open_message_stream # obsolete
|
|
|
|
|
2003-07-01 22:34:39 -04:00
|
|
|
#
|
2006-08-14 22:21:59 -04:00
|
|
|
# Authentication
|
2003-07-01 22:34:39 -04:00
|
|
|
#
|
1999-09-22 03:32:33 -04:00
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
public
|
1999-12-17 10:00:13 -05:00
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def authenticate(user, secret, authtype)
|
2006-08-14 22:21:59 -04:00
|
|
|
check_auth_method authtype
|
|
|
|
check_auth_args user, secret
|
|
|
|
funcall "auth_#{authtype || 'cram_md5'}", user, secret
|
2000-02-21 10:25:37 -05:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def auth_plain(user, secret)
|
2006-08-14 22:21:59 -04:00
|
|
|
check_auth_args user, secret
|
2003-05-02 10:35:01 -04:00
|
|
|
res = critical { get_response('AUTH PLAIN %s',
|
2003-05-24 14:24:26 -04:00
|
|
|
base64_encode("\0#{user}\0#{secret}")) }
|
2006-08-14 22:21:59 -04:00
|
|
|
raise SMTPAuthenticationError, res unless /\A2../ =~ res
|
2003-05-24 14:24:26 -04:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def auth_login(user, secret)
|
2006-08-14 22:21:59 -04:00
|
|
|
check_auth_args user, secret
|
2003-05-24 14:24:26 -04:00
|
|
|
res = critical {
|
2003-07-01 22:34:39 -04:00
|
|
|
check_response(get_response('AUTH LOGIN'), true)
|
|
|
|
check_response(get_response(base64_encode(user)), true)
|
|
|
|
get_response(base64_encode(secret))
|
2003-05-24 14:24:26 -04:00
|
|
|
}
|
2006-08-14 22:21:59 -04:00
|
|
|
raise SMTPAuthenticationError, res unless /\A2../ =~ res
|
2000-04-25 05:23:21 -04:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def auth_cram_md5(user, secret)
|
2006-08-14 22:21:59 -04:00
|
|
|
check_auth_args user, secret
|
2003-05-24 14:24:26 -04:00
|
|
|
# CRAM-MD5: [RFC2195]
|
2003-05-02 10:35:01 -04:00
|
|
|
res = nil
|
|
|
|
critical {
|
2003-07-01 22:34:39 -04:00
|
|
|
res = check_response(get_response('AUTH CRAM-MD5'), true)
|
|
|
|
challenge = res.split(/ /)[1].unpack('m')[0]
|
|
|
|
secret = Digest::MD5.digest(secret) if secret.size > 64
|
|
|
|
|
|
|
|
isecret = secret + "\0" * (64 - secret.size)
|
|
|
|
osecret = isecret.dup
|
|
|
|
0.upto(63) do |i|
|
2006-08-09 03:56:30 -04:00
|
|
|
c = isecret[i].ord ^ 0x36
|
|
|
|
isecret[i] = c.chr
|
|
|
|
c = osecret[i].ord ^ 0x5c
|
|
|
|
osecret[i] = c.chr
|
2003-07-01 22:34:39 -04:00
|
|
|
end
|
|
|
|
tmp = Digest::MD5.digest(isecret + challenge)
|
|
|
|
tmp = Digest::MD5.hexdigest(osecret + tmp)
|
|
|
|
|
|
|
|
res = get_response(base64_encode(user + ' ' + tmp))
|
2000-04-25 05:23:21 -04:00
|
|
|
}
|
2006-08-14 22:21:59 -04:00
|
|
|
raise SMTPAuthenticationError, res unless /\A2../ =~ res
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def check_auth_method(type)
|
|
|
|
mid = "auth_#{type || 'cram_md5'}"
|
|
|
|
unless respond_to?(mid, true)
|
|
|
|
raise ArgumentError, "wrong authentication type #{type}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def check_auth_args(user, secret)
|
|
|
|
unless user
|
|
|
|
raise ArgumentError, 'SMTP-AUTH requested but missing user name'
|
|
|
|
end
|
|
|
|
unless secret
|
|
|
|
raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase'
|
|
|
|
end
|
2000-04-25 05:23:21 -04:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def base64_encode(str)
|
2003-05-24 14:24:26 -04:00
|
|
|
# expects "str" may not become too long
|
|
|
|
[str].pack('m').gsub(/\s+/, '')
|
|
|
|
end
|
|
|
|
|
2003-07-01 22:34:39 -04:00
|
|
|
#
|
|
|
|
# SMTP command dispatcher
|
|
|
|
#
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
public
|
|
|
|
|
|
|
|
def starttls
|
|
|
|
getok('STARTTLS')
|
|
|
|
end
|
2003-07-01 22:34:39 -04:00
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def helo(domain)
|
2003-07-01 22:34:39 -04:00
|
|
|
getok('HELO %s', domain)
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def ehlo(domain)
|
2003-07-01 22:34:39 -04:00
|
|
|
getok('EHLO %s', domain)
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def mailfrom(from_addr)
|
|
|
|
if $SAFE > 0
|
|
|
|
raise SecurityError, 'tainted from_addr' if from_addr.tainted?
|
|
|
|
end
|
|
|
|
getok('MAIL FROM:<%s>', from_addr)
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def rcptto_list(to_addrs)
|
|
|
|
raise ArgumentError, 'mail destination not given' if to_addrs.empty?
|
2006-08-22 06:21:31 -04:00
|
|
|
to_addrs.flatten.each do |addr|
|
2006-08-14 22:21:59 -04:00
|
|
|
rcptto addr
|
|
|
|
end
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def rcptto(to_addr)
|
|
|
|
if $SAFE > 0
|
|
|
|
raise SecurityError, 'tainted to_addr' if to.tainted?
|
|
|
|
end
|
|
|
|
getok('RCPT TO:<%s>', to_addr)
|
|
|
|
end
|
|
|
|
|
|
|
|
# This method sends a message.
|
|
|
|
# If +msgstr+ is given, sends it as a message.
|
|
|
|
# If block is given, yield a message writer stream.
|
|
|
|
# You must write message before the block is closed.
|
|
|
|
#
|
|
|
|
# # Example 1 (by string)
|
|
|
|
# smtp.data(<<EndMessage)
|
|
|
|
# From: john@example.com
|
|
|
|
# To: betty@example.com
|
|
|
|
# Subject: I found a bug
|
|
|
|
#
|
|
|
|
# Check vm.c:58879.
|
|
|
|
# EndMessage
|
|
|
|
#
|
|
|
|
# # Example 2 (by block)
|
|
|
|
# smtp.data {|f|
|
|
|
|
# f.puts "From: john@example.com"
|
|
|
|
# f.puts "To: betty@example.com"
|
|
|
|
# f.puts "Subject: I found a bug"
|
|
|
|
# f.puts ""
|
|
|
|
# f.puts "Check vm.c:58879."
|
|
|
|
# }
|
|
|
|
#
|
|
|
|
def data(msgstr = nil, &block) #:yield: stream
|
|
|
|
if msgstr and block
|
|
|
|
raise ArgumentError, "message and block are exclusive"
|
|
|
|
end
|
|
|
|
unless msgstr or block
|
|
|
|
raise ArgumentError, "message or block is required"
|
|
|
|
end
|
|
|
|
res = critical {
|
|
|
|
check_response(get_response('DATA'), true)
|
|
|
|
if msgstr
|
|
|
|
@socket.write_message msgstr
|
|
|
|
else
|
|
|
|
@socket.write_message_by_block(&block)
|
|
|
|
end
|
|
|
|
recv_response()
|
|
|
|
}
|
|
|
|
check_response(res)
|
2000-03-27 10:52:27 -05:00
|
|
|
end
|
1999-09-22 03:32:33 -04:00
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
def quit
|
|
|
|
getok('QUIT')
|
2004-04-21 05:40:48 -04:00
|
|
|
end
|
2003-07-01 22:34:39 -04:00
|
|
|
|
2000-03-27 10:52:27 -05:00
|
|
|
private
|
1999-09-22 03:32:33 -04:00
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def getok(fmt, *args)
|
2003-07-01 22:34:39 -04:00
|
|
|
res = critical {
|
|
|
|
@socket.writeline sprintf(fmt, *args)
|
|
|
|
recv_response()
|
|
|
|
}
|
|
|
|
return check_response(res)
|
2003-05-02 10:35:01 -04:00
|
|
|
end
|
1999-12-29 06:14:04 -05:00
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def get_response(fmt, *args)
|
2003-05-02 10:35:01 -04:00
|
|
|
@socket.writeline sprintf(fmt, *args)
|
|
|
|
recv_response()
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
|
|
|
|
2003-05-02 10:35:01 -04:00
|
|
|
def recv_response
|
|
|
|
res = ''
|
2002-11-21 06:50:09 -05:00
|
|
|
while true
|
2003-05-02 10:35:01 -04:00
|
|
|
line = @socket.readline
|
|
|
|
res << line << "\n"
|
|
|
|
break unless line[3] == ?- # "210-PIPELINING"
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
2003-05-02 10:35:01 -04:00
|
|
|
res
|
1999-09-22 03:32:33 -04:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def check_response(res, allow_continue = false)
|
2006-08-14 22:21:59 -04:00
|
|
|
case res
|
|
|
|
when /\A2/
|
|
|
|
return res
|
|
|
|
when /\A3/
|
|
|
|
unless allow_continue
|
|
|
|
raise SMTPUnknownError,
|
|
|
|
"got response 3xx but not DATA: #{res.inspect}"
|
|
|
|
end
|
|
|
|
return res
|
|
|
|
when /\A4/
|
|
|
|
raise SMTPServerBusy, res
|
|
|
|
when /\A50/
|
|
|
|
raise SMTPSyntaxError, res
|
|
|
|
when /\A55/
|
|
|
|
raise SMTPFatalError, res
|
|
|
|
else
|
|
|
|
raise SMTPUnknownError, res
|
|
|
|
end
|
2003-07-01 22:34:39 -04:00
|
|
|
end
|
|
|
|
|
2004-04-21 05:23:56 -04:00
|
|
|
def critical(&block)
|
2003-07-01 22:34:39 -04:00
|
|
|
return '200 dummy reply code' if @error_occured
|
|
|
|
begin
|
|
|
|
return yield()
|
|
|
|
rescue Exception
|
|
|
|
@error_occured = true
|
|
|
|
raise
|
|
|
|
end
|
2003-05-02 10:35:01 -04:00
|
|
|
end
|
1999-09-22 03:32:33 -04:00
|
|
|
|
2004-03-06 12:08:21 -05:00
|
|
|
def logging(msg)
|
2004-04-21 05:40:48 -04:00
|
|
|
@debug_output << msg + "\n" if @debug_output
|
2004-03-06 12:08:21 -05:00
|
|
|
end
|
|
|
|
|
2003-07-01 22:34:39 -04:00
|
|
|
end # class SMTP
|
aamine
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb: 1.1.26.
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb:
add module Net::NetPrivate and its inner classes
{Read,Write}Adapter, Command, Socket,
SMTPCommand, POP3Command, APOPCommand, HTTPCommand
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@826 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2000-07-12 02:04:40 -04:00
|
|
|
|
2003-07-01 22:34:39 -04:00
|
|
|
SMTPSession = SMTP
|
aamine
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb: 1.1.26.
* lib/net/protocol.rb, smtp.rb, pop.rb, http.rb:
add module Net::NetPrivate and its inner classes
{Read,Write}Adapter, Command, Socket,
SMTPCommand, POP3Command, APOPCommand, HTTPCommand
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@826 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2000-07-12 02:04:40 -04:00
|
|
|
|
2006-08-14 22:21:59 -04:00
|
|
|
end
|