3 # Copyright (c) 1999-2003 Yukihiro Matsumoto.
5 # Copyright (c) 1999-2003 Minero Aoki.
7 # Written & maintained by Minero Aoki <aamine@loveruby.net>.
9 # Documented by William Webber and Minero Aoki.
11 # This program is free software. You can re-distribute and/or
12 # modify this program under the same terms as Ruby itself,
13 # Ruby Distribute License or GNU General Public License.
15 # NOTE: You can find Japanese version of this document in
16 # the doc/net directory of the standard ruby interpreter package.
18 # $Id: smtp.rb 11708 2007-02-12 23:01:19Z shyouhei $
20 # See Net::SMTP for documentation.
23 require 'net/protocol'
28 # Module mixed in to all SMTP error classes
30 # This *class* is module for some reason.
31 # In ruby 1.9.x, this module becomes a class.
34 # Represents an SMTP authentication error.
35 class SMTPAuthenticationError < ProtoAuthError
39 # Represents SMTP error code 420 or 450, a temporary error.
40 class SMTPServerBusy < ProtoServerError
44 # Represents an SMTP command syntax error (error code 500)
45 class SMTPSyntaxError < ProtoSyntaxError
49 # Represents a fatal SMTP error (error code 5xx, except for 500)
50 class SMTPFatalError < ProtoFatalError
54 # Unexpected reply code returned from server.
55 class SMTPUnknownError < ProtoUnknownError
62 # == What is This Library?
64 # This library provides functionality to send internet
65 # mail via SMTP, the Simple Mail Transfer Protocol. For details of
66 # SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt).
68 # == What is This Library NOT?
70 # This library does NOT provide functions to compose internet mails.
71 # You must create them by yourself. If you want better mail support,
72 # try RubyMail or TMail. You can get both libraries from RAA.
73 # (http://www.ruby-lang.org/en/raa.html)
75 # FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt).
79 # === Sending Messages
81 # You must open a connection to an SMTP server before sending messages.
82 # The first argument is the address of your SMTP server, and the second
83 # argument is the port number. Using SMTP.start with a block is the simplest
84 # way to do this. This way, the SMTP connection is closed automatically
85 # after the block is executed.
88 # Net::SMTP.start('your.smtp.server', 25) do |smtp|
89 # # Use the SMTP object smtp only in this block.
92 # Replace 'your.smtp.server' with your SMTP server. Normally
93 # your system manager or internet provider supplies a server
96 # Then you can send messages.
98 # msgstr = <<END_OF_MESSAGE
99 # From: Your Name <your@mail.address>
100 # To: Destination Address <someone@example.com>
101 # Subject: test message
102 # Date: Sat, 23 Jun 2001 16:26:43 +0900
103 # Message-Id: <unique.message.id.string@example.com>
105 # This is a test message.
109 # Net::SMTP.start('your.smtp.server', 25) do |smtp|
110 # smtp.send_message msgstr,
111 # 'your@mail.address',
112 # 'his_addess@example.com'
115 # === Closing the Session
117 # You MUST close the SMTP session after sending messages, by calling
118 # the #finish method:
120 # # using SMTP#finish
121 # smtp = Net::SMTP.start('your.smtp.server', 25)
122 # smtp.send_message msgstr, 'from@address', 'to@address'
125 # You can also use the block form of SMTP.start/SMTP#start. This closes
126 # the SMTP session automatically:
128 # # using block form of SMTP.start
129 # Net::SMTP.start('your.smtp.server', 25) do |smtp|
130 # smtp.send_message msgstr, 'from@address', 'to@address'
133 # I strongly recommend this scheme. This form is simpler and more robust.
137 # In almost all situations, you must provide a third argument
138 # to SMTP.start/SMTP#start. This is the domain name which you are on
139 # (the host to send mail from). It is called the "HELO domain".
140 # The SMTP server will judge whether it should send or reject
141 # the SMTP session by inspecting the HELO domain.
143 # Net::SMTP.start('your.smtp.server', 25,
144 # 'mail.from.domain') { |smtp| ... }
146 # === SMTP Authentication
148 # The Net::SMTP class supports three authentication schemes;
149 # PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
150 # To use SMTP authentication, pass extra arguments to
151 # SMTP.start/SMTP#start.
154 # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
155 # 'Your Account', 'Your Password', :plain)
157 # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
158 # 'Your Account', 'Your Password', :login)
161 # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
162 # 'Your Account', 'Your Password', :cram_md5)
166 Revision = %q$Revision: 11708 $.split[1]
168 # The default SMTP port, port 25.
169 def SMTP.default_port
174 # Creates a new Net::SMTP object.
176 # +address+ is the hostname or ip address of your SMTP
177 # server. +port+ is the port to connect to; it defaults to
180 # This method does not open the TCP connection. You can use
181 # SMTP.start instead of SMTP.new if you want to do everything
182 # at once. Otherwise, follow SMTP.new with SMTP#start.
184 def initialize( address, port = nil )
186 @port = (port || SMTP.default_port)
192 @error_occured = false
196 # Provide human-readable stringification of class state.
198 "#<#{self.class} #{@address}:#{@port} started=#{@started}>"
201 # +true+ if the SMTP object uses ESMTP (which it does by default).
207 # Set whether to use ESMTP or not. This should be done before
208 # calling #start. Note that if #start is called in ESMTP mode,
209 # and the connection fails due to a ProtocolError, the SMTP
210 # object will automatically switch to plain SMTP mode and
211 # retry (but not vice versa).
219 # The address of the SMTP server to connect to.
222 # The port number of the SMTP server to connect to.
225 # Seconds to wait while attempting to open a connection.
226 # If the connection cannot be opened within this time, a
227 # TimeoutError is raised.
228 attr_accessor :open_timeout
230 # Seconds to wait while reading one block (by one read(2) call).
231 # If the read(2) call does not complete within this time, a
232 # TimeoutError is raised.
233 attr_reader :read_timeout
235 # Set the number of seconds to wait until timing-out a read(2)
237 def read_timeout=( sec )
238 @socket.read_timeout = sec if @socket
243 # WARNING: This method causes serious security holes.
244 # Use this method for only debugging.
246 # Set an output stream for debug logging.
247 # You must call this before #start.
250 # smtp = Net::SMTP.new(addr, port)
251 # smtp.set_debug_output $stderr
252 # smtp.start do |smtp|
256 def set_debug_output( arg )
261 # SMTP session control
265 # Creates a new Net::SMTP object and connects to the server.
267 # This method is equivalent to:
269 # Net::SMTP.new(address, port).start(helo_domain, account, password, authtype)
273 # Net::SMTP.start('your.smtp.server') do |smtp|
274 # smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
279 # If called with a block, the newly-opened Net::SMTP object is yielded
280 # to the block, and automatically closed when the block finishes. If called
281 # without a block, the newly-opened Net::SMTP object is returned to
282 # the caller, and it is the caller's responsibility to close it when
287 # +address+ is the hostname or ip address of your smtp server.
289 # +port+ is the port to connect to; it defaults to port 25.
291 # +helo+ is the _HELO_ _domain_ provided by the client to the
292 # server (see overview comments); it defaults to 'localhost.localdomain'.
294 # The remaining arguments are used for SMTP authentication, if required
295 # or desired. +user+ is the account name; +secret+ is your password
296 # or other authentication token; and +authtype+ is the authentication
297 # type, one of :plain, :login, or :cram_md5. See the discussion of
298 # SMTP Authentication in the overview notes.
302 # This method may raise:
304 # * Net::SMTPAuthenticationError
305 # * Net::SMTPServerBusy
306 # * Net::SMTPSyntaxError
307 # * Net::SMTPFatalError
308 # * Net::SMTPUnknownError
312 def SMTP.start( address, port = nil,
313 helo = 'localhost.localdomain',
314 user = nil, secret = nil, authtype = nil,
315 &block) # :yield: smtp
316 new(address, port).start(helo, user, secret, authtype, &block)
319 # +true+ if the SMTP session has been started.
325 # Opens a TCP connection and starts the SMTP session.
329 # +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see
330 # the discussion in the overview notes.
332 # If both of +user+ and +secret+ are given, SMTP authentication
333 # will be attempted using the AUTH command. +authtype+ specifies
334 # the type of authentication to attempt; it must be one of
335 # :login, :plain, and :cram_md5. See the notes on SMTP Authentication
340 # When this methods is called with a block, the newly-started SMTP
341 # object is yielded to the block, and automatically closed after
342 # the block call finishes. Otherwise, it is the caller's
343 # responsibility to close the session when finished.
347 # This is very similar to the class method SMTP.start.
350 # smtp = Net::SMTP.new('smtp.mail.server', 25)
351 # smtp.start(helo_domain, account, password, authtype) do |smtp|
352 # smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
355 # The primary use of this method (as opposed to SMTP.start)
356 # is probably to set debugging (#set_debug_output) or ESMTP
357 # (#esmtp=), which must be done before the session is
362 # If session has already been started, an IOError will be raised.
364 # This method may raise:
366 # * Net::SMTPAuthenticationError
367 # * Net::SMTPServerBusy
368 # * Net::SMTPSyntaxError
369 # * Net::SMTPFatalError
370 # * Net::SMTPUnknownError
374 def start( helo = 'localhost.localdomain',
375 user = nil, secret = nil, authtype = nil ) # :yield: smtp
378 do_start(helo, user, secret, authtype)
384 do_start(helo, user, secret, authtype)
389 def do_start( helodomain, user, secret, authtype )
390 raise IOError, 'SMTP session already started' if @started
391 check_auth_args user, secret, authtype if user or secret
393 @socket = InternetMessageIO.old_open(@address, @port,
394 @open_timeout, @read_timeout,
396 check_response(critical { recv_response() })
406 @error_occured = false
411 authenticate user, secret, authtype if user
414 @socket.close if not @started and @socket and not @socket.closed?
418 # Finishes the SMTP session and closes TCP connection.
419 # Raises IOError if not started.
421 raise IOError, 'not yet started' unless started?
426 quit if @socket and not @socket.closed? and not @error_occured
429 @error_occured = false
430 @socket.close if @socket and not @socket.closed?
442 # Sends +msgstr+ as a message. Single CR ("\r") and LF ("\n") found
443 # in the +msgstr+, are converted into the CR LF pair. You cannot send a
444 # binary message with this method. +msgstr+ should include both
445 # the message headers and body.
447 # +from_addr+ is a String representing the source mail address.
449 # +to_addr+ is a String or Strings or Array of Strings, representing
450 # the destination mail address or addresses.
454 # Net::SMTP.start('smtp.example.com') do |smtp|
455 # smtp.send_message msgstr,
456 # 'from@example.com',
457 # ['dest@example.com', 'dest2@example.com']
462 # This method may raise:
464 # * Net::SMTPServerBusy
465 # * Net::SMTPSyntaxError
466 # * Net::SMTPFatalError
467 # * Net::SMTPUnknownError
471 def send_message( msgstr, from_addr, *to_addrs )
472 send0(from_addr, to_addrs.flatten) {
473 @socket.write_message msgstr
477 alias send_mail send_message
478 alias sendmail send_message # obsolete
481 # Opens a message writer stream and gives it to the block.
482 # The stream is valid only in the block, and has these methods:
484 # puts(str = ''):: outputs STR and CR LF.
485 # print(str):: outputs STR.
486 # printf(fmt, *args):: outputs sprintf(fmt,*args).
487 # write(str):: outputs STR and returns the length of written bytes.
488 # <<(str):: outputs STR and returns self.
490 # If a single CR ("\r") or LF ("\n") is found in the message,
491 # it is converted to the CR LF pair. You cannot send a binary
492 # message with this method.
496 # +from_addr+ is a String representing the source mail address.
498 # +to_addr+ is a String or Strings or Array of Strings, representing
499 # the destination mail address or addresses.
503 # Net::SMTP.start('smtp.example.com', 25) do |smtp|
504 # smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f|
505 # f.puts 'From: from@example.com'
506 # f.puts 'To: dest@example.com'
507 # f.puts 'Subject: test message'
509 # f.puts 'This is a test message.'
515 # This method may raise:
517 # * Net::SMTPServerBusy
518 # * Net::SMTPSyntaxError
519 # * Net::SMTPFatalError
520 # * Net::SMTPUnknownError
524 def open_message_stream( from_addr, *to_addrs, &block ) # :yield: stream
525 send0(from_addr, to_addrs.flatten) {
526 @socket.write_message_by_block(&block)
530 alias ready open_message_stream # obsolete
534 def send0( from_addr, to_addrs )
535 raise IOError, 'closed session' unless @socket
536 raise ArgumentError, 'mail destination not given' if to_addrs.empty?
538 raise SecurityError, 'tainted from_addr' if from_addr.tainted?
539 to_addrs.each do |to|
540 raise SecurityError, 'tainted to_addr' if to.tainted?
545 to_addrs.each do |to|
549 check_response(get_response('DATA'), true)
562 def check_auth_args( user, secret, authtype )
563 raise ArgumentError, 'both user and secret are required'\
564 unless user and secret
565 auth_method = "auth_#{authtype || 'cram_md5'}"
566 raise ArgumentError, "wrong auth type #{authtype}"\
567 unless respond_to?(auth_method, true)
570 def authenticate( user, secret, authtype )
571 __send__("auth_#{authtype || 'cram_md5'}", user, secret)
574 def auth_plain( user, secret )
575 res = critical { get_response('AUTH PLAIN %s',
576 base64_encode("\0#{user}\0#{secret}")) }
577 raise SMTPAuthenticationError, res unless /\A2../ === res
580 def auth_login( user, secret )
582 check_response(get_response('AUTH LOGIN'), true)
583 check_response(get_response(base64_encode(user)), true)
584 get_response(base64_encode(secret))
586 raise SMTPAuthenticationError, res unless /\A2../ === res
589 def auth_cram_md5( user, secret )
590 # CRAM-MD5: [RFC2195]
593 res = check_response(get_response('AUTH CRAM-MD5'), true)
594 challenge = res.split(/ /)[1].unpack('m')[0]
595 secret = Digest::MD5.digest(secret) if secret.size > 64
597 isecret = secret + "\0" * (64 - secret.size)
598 osecret = isecret.dup
603 tmp = Digest::MD5.digest(isecret + challenge)
604 tmp = Digest::MD5.hexdigest(osecret + tmp)
606 res = get_response(base64_encode(user + ' ' + tmp))
608 raise SMTPAuthenticationError, res unless /\A2../ === res
611 def base64_encode( str )
612 # expects "str" may not become too long
613 [str].pack('m').gsub(/\s+/, '')
617 # SMTP command dispatcher
623 getok('HELO %s', domain)
627 getok('EHLO %s', domain)
630 def mailfrom( fromaddr )
631 getok('MAIL FROM:<%s>', fromaddr)
635 getok('RCPT TO:<%s>', to)
648 def getok( fmt, *args )
650 @socket.writeline sprintf(fmt, *args)
653 return check_response(res)
656 def get_response( fmt, *args )
657 @socket.writeline sprintf(fmt, *args)
664 line = @socket.readline
666 break unless line[3] == ?- # "210-PIPELINING"
671 def check_response( res, allow_continue = false )
672 return res if /\A2/ === res
673 return res if allow_continue and /\A3/ === res
675 when /\A4/ then SMTPServerBusy
676 when /\A50/ then SMTPSyntaxError
677 when /\A55/ then SMTPFatalError
678 else SMTPUnknownError
683 def critical( &block )
684 return '200 dummy reply code' if @error_occured
688 @error_occured = true