OSDN Git Service

FIRST REPOSITORY
[eos/hostdependOTHERS.git] / ALPHALINUX5 / util / ALPHALINUX5 / lib / ruby / 1.6 / net / smtp.rb
1 =begin
2
3 = net/smtp.rb version 1.1.35
4
5 Copyright (c) 1999-2001 Yukihiro Matsumoto
6
7 written & maintained by Minero Aoki <aamine@loveruby.net>
8
9 This program is free software. You can re-distribute and/or
10 modify this program under the same terms as Ruby itself,
11 Ruby Distribute License or GNU General Public License.
12
13 NOTE: You can get Japanese version of this document from
14 Ruby Documentation Project (RDP):
15 ((<URL:http://www.ruby-lang.org/~rubikitch/RDP.cgi>))
16
17 == What is This Module?
18
19 This module provides your program the functions to send internet
20 mail via SMTP, Simple Mail Transfer Protocol. For details of
21 SMTP itself, refer [RFC2821] ((<URL:http://www.ietf.org/rfc/rfc2821.txt>)).
22
23 == What This Module is NOT?
24
25 This module does NOT provide the functions to compose internet
26 mail. You must create it by yourself. For details of internet mail
27 format, see [RFC2822] ((<URL:http://www.ietf.org/rfc/rfc2822.txt>)).
28
29 == Examples
30
31 === Sending Mail
32
33 You must open connection to SMTP server before sending mails.
34 First argument is the address of SMTP server, and second argument
35 is port number. Using SMTP.start with block is the most simple way
36 to do it. SMTP Connection is closed automatically after block is
37 executed.
38
39     require 'net/smtp'
40     Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
41       # use smtp object only in this block
42     }
43
44 Replace 'your.smtp.server' by your SMTP server. Normally
45 your system manager or internet provider is supplying a server
46 for you.
47
48 Then you can send mail.
49
50     require 'net/smtp'
51
52     Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
53       smtp.send_mail <<EndOfMail, 'your@mail.address', 'to@some.domain'
54     From: Your Name <your@mail.address>
55     To: Dest Address <to@some.domain>
56     Subject: test mail
57     Date: Sat, 23 Jun 2001 16:26:43 +0900
58     Message-Id: <unique.message.id.string@some.domain>
59
60     This is test mail.
61     EndOfMail
62     }
63
64 === Sending Mails from Any Sources
65
66 In an example above I sent mail from String (here document literal).
67 SMTP#send_mail accepts any objects which has "each" method
68 like File and Array.
69
70     require 'net/smtp'
71     Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
72       File.open( 'Mail/draft/1' ) {|f|
73         smtp.send_mail f, 'your@mail.address', 'to@some.domain'
74       }
75     }
76
77 === Giving "Hello" Domain
78
79 If your machine does not have canonical host name, maybe you
80 must designate the third argument of SMTP.start.
81
82     Net::SMTP.start( 'your.smtp.server', 25,
83                      'mail.from.domain' ) {|smtp|
84
85 This argument gives MAILFROM domain, the domain name that
86 you send mail from. SMTP server might judge if he (or she?)
87 send or reject SMTP session by this data.
88
89 == class Net::SMTP
90
91 === Class Methods
92
93 : new( address = 'localhost', port = 25 )
94     creates a new Net::SMTP object.
95
96 : start( address = 'localhost', port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil )
97 : start( address = 'localhost', port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... }
98     is equal to
99         Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
100
101         # example
102         Net::SMTP.start( 'your.smtp.server' ) {
103           smtp.send_mail mail_string, 'from@mail.address', 'dest@mail.address'
104         }
105
106 === Instance Methods
107
108 : start( helo_domain = <local host name>, account = nil, password = nil, authtype = nil )
109 : start( helo_domain = <local host name>, account = nil, password = nil, authtype = nil ) {|smtp| .... }
110     opens TCP connection and starts SMTP session.
111     If protocol had been started, do nothing and return false.
112     HELO_DOMAIN is a domain that you'll dispatch mails from.
113
114     When this methods is called with block, give a SMTP object to block and
115     close session after block call finished.
116
117     If both of account and password are given, is trying to get
118     authentication by using AUTH command. :plain or :cram_md5 is
119     allowed for AUTHTYPE.
120
121 : active?
122     true if SMTP session is started.
123
124 : address
125     the address to connect
126
127 : port
128     the port number to connect
129
130 : open_timeout
131 : open_timeout=(n)
132     seconds to wait until connection is opened.
133     If SMTP object cannot open a conection in this seconds,
134     it raises TimeoutError exception.
135
136 : read_timeout
137 : read_timeout=(n)
138     seconds to wait until reading one block (by one read(1) call).
139     If SMTP object cannot open a conection in this seconds,
140     it raises TimeoutError exception.
141
142 : finish
143     finishes SMTP session.
144     If SMTP session had not started, do nothing and return false.
145
146 : send_mail( mailsrc, from_addr, *to_addrs )
147     This method sends MAILSRC as mail. A SMTP object read strings
148     from MAILSRC by calling "each" iterator, with converting them
149     into CRLF ("\r\n") terminated string when write.
150
151     FROM_ADDR must be a String, representing source mail address.
152     TO_ADDRS must be Strings or an Array of Strings, representing
153     destination mail addresses.
154
155         # example
156         Net::SMTP.start( 'your.smtp.server' ) {|smtp|
157           smtp.send_mail mail_string,
158                          'from@mail.address',
159                          'dest@mail.address' 'dest2@mail.address'
160         }
161
162 : ready( from_addr, *to_addrs ) {|adapter| .... }
163     This method stands by the SMTP object for sending mail and
164     give adapter object to the block. ADAPTER accepts only "write"
165     method.
166
167     FROM_ADDR must be a String, representing source mail address.
168     TO_ADDRS must be Strings or an Array of Strings, representing
169     destination mail addresses.
170
171         # example
172         Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
173           smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) do |adapter|
174             adapter.write str1
175             adapter.write str2
176             adapter.write str3
177           end
178         }
179
180 == Exceptions
181
182 SMTP objects raise these exceptions:
183 : Net::ProtoSyntaxError
184     syntax error (errno.500)
185 : Net::ProtoFatalError
186     fatal error (errno.550)
187 : Net::ProtoUnknownError
188     unknown error. (is probably bug)
189 : Net::ProtoServerBusy
190     temporary error (errno.420/450)
191
192 =end
193
194 require 'net/protocol'
195 require 'md5'
196
197
198 module Net
199
200
201   class SMTP < Protocol
202
203     protocol_param :port,         '25'
204     protocol_param :command_type, '::Net::NetPrivate::SMTPCommand'
205
206
207     def initialize( addr = nil, port = nil )
208       super
209       @esmtp = true
210     end
211
212     attr :esmtp
213
214     def send_mail( mailsrc, from_addr, *to_addrs )
215       do_ready from_addr, to_addrs.flatten
216       @command.write_mail mailsrc, nil
217     end
218
219     alias sendmail send_mail
220
221     def ready( from_addr, *to_addrs, &block )
222       do_ready from_addr, to_addrs.flatten
223       @command.write_mail nil, block
224     end
225
226
227     private
228
229
230     def do_ready( from_addr, to_addrs )
231       if to_addrs.empty? then
232         raise ArgumentError, 'mail destination does not given'
233       end
234       @command.mailfrom from_addr
235       @command.rcpt to_addrs
236       @command.data
237     end
238
239     def do_start( helodom = nil,
240                   user = nil, secret = nil, authtype = nil )
241       helodom ||= ::Socket.gethostname
242       unless helodom then
243         raise ArgumentError,
244           "cannot get localhost name; try 'smtp.start(local_host_name)'"
245       end
246
247       begin
248         if @esmtp then
249           @command.ehlo helodom
250         else
251           @command.helo helodom
252         end
253       rescue ProtocolError
254         if @esmtp then
255           @esmtp = false
256           @command.error_ok
257           retry
258         else
259           raise
260         end
261       end
262
263       if user or secret then
264         (user and secret) or
265             raise ArgumentError, 'both of account and password are required'
266
267         mid = 'auth_' + (authtype || 'cram_md5').to_s
268         @command.respond_to? mid or
269             raise ArgumentError, "wrong auth type #{authtype.to_s}"
270
271         @command.__send__ mid, user, secret
272       end
273     end
274
275   end
276
277   SMTPSession = SMTP
278
279
280
281   module NetPrivate
282
283
284   class SMTPCommand < Command
285
286     def initialize( sock )
287       super
288       critical {
289         check_reply SuccessCode
290       }
291     end
292
293
294     def helo( fromdom )
295       critical {
296         getok sprintf( 'HELO %s', fromdom )
297       }
298     end
299
300
301     def ehlo( fromdom )
302       critical {
303         getok sprintf( 'EHLO %s', fromdom )
304       }
305     end
306
307
308     # "PLAIN" authentication [RFC2554]
309     def auth_plain( user, secret )
310       critical {
311         getok sprintf( 'AUTH PLAIN %s',
312                        ["\0#{user}\0#{secret}"].pack('m').chomp )
313       }
314     end
315
316     # "CRAM-MD5" authentication [RFC2195]
317     def auth_cram_md5( user, secret )
318       critical {
319         rep = getok( 'AUTH CRAM-MD5', ContinueCode )
320         challenge = rep.msg.split(' ')[1].unpack('m')[0]
321         secret = MD5.new( secret ).digest if secret.size > 64
322
323         isecret = secret + "\0" * (64 - secret.size)
324         osecret = isecret.dup
325         0.upto( 63 ) do |i|
326           isecret[i] ^= 0x36
327           osecret[i] ^= 0x5c
328         end
329         tmp = MD5.new( isecret + challenge ).digest
330         tmp = MD5.new( osecret + tmp ).hexdigest
331
332         getok [user + ' ' + tmp].pack('m').chomp
333       }
334     end
335
336
337     def mailfrom( fromaddr )
338       critical {
339         getok sprintf( 'MAIL FROM:<%s>', fromaddr )
340       }
341     end
342
343
344     def rcpt( toaddrs )
345       toaddrs.each do |i|
346         critical {
347           getok sprintf( 'RCPT TO:<%s>', i )
348         }
349       end
350     end
351
352
353     def data
354       return unless begin_critical
355       getok 'DATA', ContinueCode
356     end
357
358     def write_mail( mailsrc, block )
359       @socket.write_pendstr mailsrc, block
360       check_reply SuccessCode
361       end_critical
362     end
363
364
365     def quit
366       critical {
367         getok 'QUIT'
368       }
369     end
370
371
372     private
373
374
375     def get_reply
376       arr = read_reply
377       stat = arr[0][0,3]
378
379       klass = case stat[0]
380               when ?2 then SuccessCode
381               when ?3 then ContinueCode
382               when ?4 then ServerErrorCode
383               when ?5 then
384                 case stat[1]
385                 when ?0 then SyntaxErrorCode
386                 when ?3 then AuthErrorCode
387                 when ?5 then FatalErrorCode
388                 end
389               end
390       klass ||= UnknownCode
391
392       Response.new( klass, stat, arr.join('') )
393     end
394
395
396     def read_reply
397       arr = []
398       while true do
399         str = @socket.readline
400         break unless str[3] == ?-   # ex: "210-..."
401         arr.push str
402       end
403       arr.push str
404
405       arr
406     end
407
408   end
409
410
411   end   # module Net::NetPrivate
412
413 end   # module Net