diff --git a/ChangeLog b/ChangeLog index a31aac5592..e07f04284f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Wed Jun 27 08:53:26 2001 Minero Aoki <#e(:global).g(:email)>\n + * lib/net/pop.rb: new methods POP3.auth_only, POP3#auth_only + + * lib/net/http.rb: HTTP.Proxy returns self if ADDRESS is nil. + + * lib/net/protocol.rb: new method ProtocolError#response + + * lib/net/protocol.rb,smtp.rb,pop.rb,http.rb: add document. + Sat Jun 23 18:28:52 2001 Akinori MUSHA * ext/readline/readline.c (readline_event): a non-void function diff --git a/lib/net/http.rb b/lib/net/http.rb index bbe98274f4..6a8fc5f862 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1,125 +1,304 @@ =begin -= net/http.rb version 1.2.0 += net/http.rb version 1.2.1 - Copyright (C) 1999-2001 Yukihiro Matsumoto +Copyright (c) 1999-2001 Yukihiro Matsumoto - written & maintained by Minero Aoki - This file is derived from "http-access.rb". +written & maintained by Minero Aoki +This file is derived from "http-access.rb". - This program is free software. You can re-distribute and/or - modify this program under the same terms as Ruby itself, - GNU General Public License or Ruby License. +This program is free software. You can re-distribute and/or +modify this program under the same terms as Ruby itself, +Ruby Distribute License or GNU General Public License. - Japanese version of this document is in "net" full package. - You can get it from RAA (Ruby Application Archive). RAA is: - http://www.ruby-lang.org/en/raa.html +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +(()) +== What is this module? + +This module provide your program the functions to access WWW +documents via HTTP, Hyper Text Transfer Protocol version 1.1. +For details of HTTP, refer [RFC2616] +(()). + +== Examples + +=== Getting Document From Server + +Be care to ',' (comma) putted after "response". +This is required for feature compatibility. + + require 'net/http' + Net::HTTP.start( 'some.www.server', 80 ) {|http| + response , = http.get('/index.html') + puts response.body + } + +(shorter version) + + require 'net/http' + Net::HTTP.get_print 'some.www.server', '/index.html' + +=== Posting Form Data + + require 'net/http' + Net::HTTP.start( 'some.www.server', 80 ) {|http| + response , = http.post( '/cgi-bin/any.rhtml', + 'querytype=subject&target=ruby' ) + } + +=== Accessing via Proxy + +Net::HTTP.Proxy() creates http proxy class. It has same +methods of Net::HTTP but its instances always connect to +proxy, instead of given host. + + require 'net/http' + + $proxy_addr = 'your.proxy.addr' + $proxy_port = 8080 + : + Net::HTTP::Proxy($proxy_addr, $proxy_port).start( 'some.www.server' ) {|http| + # always connect to your.proxy.addr:8080 + : + } + +Since Net::HTTP.Proxy() returns Net::HTTP itself when $proxy_addr is nil, +there's no need to change code if there's proxy or not. + +=== Redirect + + require 'net/http' + Net::HTTP.version_1_1 + + host = 'www.ruby-lang.org' + begin + Net::HTTP.start( host, 80 ) {|http| + response , = http.get('/') + } + rescue Net::ProtoRetriableError => err + if m = %r.match( err.response['location'] ) then + host = m[1].strip + retry + end + end + +NOTE: This code is using ad-hoc way to extract host name, but in future +URI class will be included in ruby standard library. + +=== Basic Authentication + + require 'net/http' + + Net::HTTP.start( 'auth.some.domain' ) {|http| + response , = http.get( '/need-auth.cgi', + 'Authentication' => ["#{account}:#{password}"].pack('m').strip ) + print response.body + } + +In version 1.2 (Ruby 1.7 or later), you can write like this: + + require 'net/http' + + req = Net::HTTP::Get.new('/need-auth.cgi') + req.basic_auth 'account', 'password' + Net::HTTP.start( 'auth.some.domain' ) {|http| + response = http.request( req ) + print response.body + } + +== Switching Net::HTTP versions + +You can use old Net::HTTP (in Ruby 1.6) features by calling +HTTP.version_1_1. And calling Net::HTTP.version_1_2 allows +you to use 1.2 features again. + + # example + Net::HTTP.start {|http1| ...(http1 has 1.2 features)... } + + Net::HTTP.version_1_1 + Net::HTTP.start {|http2| ...(http2 has 1.1 features)... } + + Net::HTTP.version_1_2 + Net::HTTP.start {|http3| ...(http3 has 1.2 features)... } + +Yes, this is not thread-safe. == class Net::HTTP === Class Methods : new( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) - creates a new Net::HTTP object. - If proxy_addr is given, this method is equals to - Net::HTTP::Proxy(proxy_addr,proxy_port). + creates a new Net::HTTP object. + If proxy_addr is given, this method is equals to + Net::HTTP::Proxy(proxy_addr,proxy_port). : start( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) : start( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil ) {|http| .... } - is equals to + is equals to - Net::HTTP.new( address, port, proxy_addr, proxy_port ).start(&block) + Net::HTTP.new(address, port, proxy_addr, proxy_port).start(&block) : get( address, path, port = 80 ) - gets entity body from path and returns it. - return value is a String. + gets entity body from path and returns it. + return value is a String. : get_print( address, path, port = 80 ) - gets entity body from path and print it. - return value is an entity body (a String). + gets entity body from path and print it. + return value is an entity body (a String). -: Proxy( address, port ) - creates a HTTP proxy class. - Arguments are address/port of proxy host. - You can replace HTTP class by this proxy class. +: Proxy( address, port = 80 ) + creates a HTTP proxy class. + Arguments are address/port of proxy host. + You can replace HTTP class by this proxy class. - # example - proxy_http = HTTP::Proxy( 'proxy.foo.org', 8080 ) - : - proxy_http.start( 'www.ruby-lang.org' ) do |http| - # connecting proxy.foo.org:8080 - : - end + If ADDRESS is nil, this method returns self (Net::HTTP class). + + # example + proxy_class = Net::HTTP::Proxy( 'proxy.foo.org', 8080 ) + : + proxy_class.start( 'www.ruby-lang.org' ) do |http| + # connecting proxy.foo.org:8080 + : + end : proxy_class? - If self is HTTP, false. - If self is a class which was created by HTTP::Proxy(), true. + If self is HTTP, false. + If self is a class which was created by HTTP::Proxy(), true. : port - HTTP default port (80). - + HTTP default port (80). === Instance Methods : start : start {|http| .... } - creates a new Net::HTTP object and starts HTTP session. + creates a new Net::HTTP object and starts HTTP session. - When this method is called with block, gives HTTP object to block - and close HTTP session after block call finished. + When this method is called with block, gives a HTTP object to block + and close the HTTP session after block call finished. + +: active? + true if HTTP session is started. + +: address + the address to connect + +: port + the port number to connect + +: open_timeout +: open_timeout=(n) + seconds to wait until connection is opened. + If HTTP object cannot open a conection in this seconds, + it raises TimeoutError exception. + +: read_timeout +: read_timeout=(n) + seconds to wait until reading one block (by one read(1) call). + If HTTP object cannot open a conection in this seconds, + it raises TimeoutError exception. + +: finish + finishes HTTP session. + If HTTP session had not started, do nothing and return false. : proxy? - true if self is a HTTP proxy class + true if self is a HTTP proxy class : proxy_address - address of proxy host. If self is not a proxy, nil. + address of proxy host. If self is not a proxy, nil. : proxy_port - port number of proxy host. If self is not a proxy, nil. + port number of proxy host. If self is not a proxy, nil. : get( path, header = nil, dest = '' ) : get( path, header = nil ) {|str| .... } - gets data from "path" on connecting host. - "header" must be a Hash like { 'Accept' => '*/*', ... }. - Response body is written into "dest" by using "<<" method. - This method returns Net::HTTPResponse object. + gets data from "path" on connecting host. + "header" must be a Hash like { 'Accept' => '*/*', ... }. + Response body is written into "dest" by using "<<" method. + This method returns Net::HTTPResponse object. - # example - response = http.get( '/index.html' ) + If called with block, give a part String of entity body. - If called with block, give a part String of entity body. + In version 1.1, this method might raises exception for also + 3xx (redirect). On the case you can get response object by + err.response. + + In version 1.2, this method never raises exception. + + # version 1.1 (Ruby 1.6) + response, body = http.get( '/index.html' ) + + # version 1.2 (Ruby 1.7 or later) + response = http.get( '/index.html' ) + + # compatible in both version + response , = http.get( '/index.html' ) + response.body + + # using block + File.open( 'save.txt', 'w' ) {|f| + http.get( '/~foo/', nil ) do |str| + f.write str + end + } + # some effect + File.open( 'save.txt', 'w' ) {|f| + http.get '/~foo/', nil, f + } : head( path, header = nil ) - gets only header from "path" on connecting host. - "header" is a Hash like { 'Accept' => '*/*', ... }. - This method returns a Net::HTTPResponse object. - You can http header from this object like: + gets only header from "path" on connecting host. + "header" is a Hash like { 'Accept' => '*/*', ... }. + This method returns a Net::HTTPResponse object. + You can http header from this object like: - response['content-length'] #-> '2554' - response['content-type'] #-> 'text/html' - response['Content-Type'] #-> 'text/html' - response['CoNtEnT-tYpe'] #-> 'text/html' + response = nil + Net::HTTP.start( 'some.www.server', 80 ) {|http| + response = http.head( '/index.html' ) + } + response['content-length'] #-> '2554' + response['content-type'] #-> 'text/html' + response['Content-Type'] #-> 'text/html' + response['CoNtEnT-tYpe'] #-> 'text/html' : post( path, data, header = nil, dest = '' ) : post( path, data, header = nil ) {|str| .... } - posts "data" (must be String) to "path". - If the body exists, also gets entity body. - Response body is written into "dest" by using "<<" method. - "header" must be a Hash like { 'Accept' => '*/*', ... }. - This method returns Net::HTTPResponse object. + posts "data" (must be String) to "path". + If the body exists, also gets entity body. + Response body is written into "dest" by using "<<" method. + "header" must be a Hash like { 'Accept' => '*/*', ... }. + This method returns Net::HTTPResponse object. - If called with block, gives a part of entity body string. + If called with block, gives a part of entity body string. + # version 1.1 + response, body = http.post( '/index.html', 'querytype=subject&target=ruby' ) + # version 1.2 + response = http.post( '/index.html', 'querytype=subject&target=ruby' ) + # compatible for both version + response , = http.post( '/index.html', 'querytype=subject&target=ruby' ) -: request( request, [src] ) + # using block + File.open( 'save.html', 'w' ) {|f| + http.post( '/index.html', 'querytype=subject&target=ruby' ) do |str| + f.write str + end + } + # same effect + File.open( 'save.html', 'w' ) {|f| + http.post '/index.html', 'querytype=subject&target=ruby', nil, f + } + +: request( request, [data] ) : request( request, [src] ) {|response| .... } - sends REQUEST to (remote) http server. This method also writes - string from SRC before it if REQUEST is a post/put request. - (giving SRC for get/head request causes ArgumentError.) - - If called with block, gives a HTTP response object to the block. + sends REQUEST to (remote) http server. This method also writes + string from DATA string if REQUEST is a post/put request. + (giving DATA for get/head request causes ArgumentError.) + If called with block, gives a HTTPResponse object to the block. == class Net::HTTP::Get, Head, Post @@ -131,7 +310,7 @@ entity path. All arguments named "key" is case-insensitive. : new creats HTTP request object. -=== Methods +=== Instance Methods : self[ key ] returns the header field corresponding to the case-insensitive key. @@ -140,13 +319,32 @@ entity path. All arguments named "key" is case-insensitive. : self[ key ] = val sets the header field corresponding to the case-insensitive key. +: each {|name, val| .... } + iterates for each field name and value pair. + +: basic_auth( account, password ) + set Authorization: header for basic auth. + +: range + returns a Range object which represents Range: header field. + +: range = r +: set_range( i, len ) + set Range: header from Range (arg r) or beginning index and + length from it (arg i&len). + +: content_length + returns a Integer object which represents Content-Length: header field. + +: content_range + returns a Range object which represents Content-Range: header field. == class Net::HTTPResponse HTTP response class. This class wraps response header and entity. All arguments named KEY is case-insensitive. -=== Methods +=== Instance Methods : self[ key ] returns the header field corresponding to the case-insensitive key. @@ -166,7 +364,7 @@ All arguments named KEY is case-insensitive. iterates for each field name and value pair. : canonical_each {|name,value| .... } - iterates for each canonical field name and value pair. + iterates for each "canonical" field name and value pair. : code HTTP result code string. For example, '302'. @@ -175,34 +373,18 @@ All arguments named KEY is case-insensitive. HTTP result message. For example, 'Not Found'. : read_body( dest = '' ) - gets response body and write it into DEST using "<<" method. + gets entity body and write it into DEST using "<<" method. If this method is called twice or more, nothing will be done and returns first DEST. : read_body {|str| .... } - gets response body little by little and pass it to block. + gets entity body little by little and pass it to block. : body - response body. If #read_body has been called, this method - returns arg of #read_body, DEST. Else gets body as String - and returns it. + response body. If #read_body has been called, this method returns + arg of #read_body DEST. Else gets body as String and returns it. -== Switching Net::HTTP versions - -You can use Net::HTTP 1.1 features by calling HTTP.version_1_1 . -And calling Net::HTTP.version_1_2 allows you to use 1.2 features -again. - - # example - HTTP.start {|http1| ...(http1 has 1.2 features)... } - - HTTP.version_1_1 - HTTP.start {|http2| ...(http2 has 1.1 features)... } - - HTTP.version_1_2 - HTTP.start {|http3| ...(http3 has 1.2 features)... } - =end require 'net/protocol' @@ -224,18 +406,13 @@ module Net protocol_param :port, '80' - def initialize( addr = nil, port = nil ) super - @proxy_address = nil - @proxy_port = nil - @curr_http_version = HTTPVersion @seems_1_0_server = false end - private def conn_command( sock ) @@ -255,7 +432,11 @@ module Net class << self def Proxy( p_addr, p_port = nil ) - ProxyMod.create_proxy_class( p_addr, p_port || self.port ) + if p_addr then + ProxyMod.create_proxy_class( p_addr, p_port || self.port ) + else + self + end end alias orig_new new @@ -271,32 +452,34 @@ module Net new( address, port, p_addr, p_port ).start( &block ) end + @is_proxy_class = false + @proxy_addr = nil + @proxy_port = nil + def proxy_class? - false + @is_proxy_class end - def proxy_address - nil - end - - def proxy_port - nil - end + attr_reader :proxy_address + attr_reader :proxy_port end def proxy? - false + type.proxy? end def proxy_address - nil + type.proxy_address end def proxy_port - nil + type.proxy_port end + alias proxyaddr proxy_address + alias proxyport proxy_port + def edit_path( path ) path end @@ -304,52 +487,22 @@ module Net module ProxyMod - class << self - - def create_proxy_class( p_addr, p_port ) - mod = self - klass = Class.new( HTTP ) - klass.module_eval { - include mod - @proxy_address = p_addr - @proxy_port = p_port - } - def klass.proxy_class? - true - end - - def klass.proxy_address - @proxy_address - end - - def klass.proxy_port - @proxy_port - end - - klass - end - + def self.create_proxy_class( p_addr, p_port ) + mod = self + klass = Class.new( HTTP ) + klass.module_eval { + include mod + @is_proxy = true + @proxy_address = p_addr + @proxy_port = p_port + } + klass end - def initialize( addr, port ) - super - @proxy_address = type.proxy_address - @proxy_port = type.proxy_port - end - - attr_reader :proxy_address, :proxy_port - - alias proxyaddr proxy_address - alias proxyport proxy_port - - def proxy? - true - end - private def conn_socket( addr, port ) - super @proxy_address, @proxy_port + super proxy_address, proxy_port end def edit_path( path ) @@ -363,7 +516,11 @@ module Net # for backward compatibility # - @@newimpl = true + if Version < '1.2.0' then ###noupdate + @@newimpl = false + else + @@newimpl = true + end class << self @@ -375,6 +532,10 @@ module Net @@newimpl = false end + def is_version_1_2? + @@newimpl + end + private def setvar( obj ) @@ -662,15 +823,15 @@ module Net d1 = m[1].to_i d2 = m[2].to_i - if m[1] and m[2] then arr.push (d1 .. d2) - elsif m[1] then arr.push (d1 .. -1) - elsif m[2] then arr.push (-d2 .. -1) + if m[1] and m[2] then arr.push d1..d2 + elsif m[1] then arr.push d1..-1 + elsif m[2] then arr.push -d2..-1 else raise HTTPHeaderSyntaxError, 'range is not specified' end end - return *arr + return arr end def range=( r, fin = nil ) @@ -906,7 +1067,10 @@ module Net @buf.concat s end - alias << write + def <<( s ) + @buf.concat s + self + end def terminate ret = @buf @@ -1028,7 +1192,7 @@ module Net end def value - SuccessCode === self or error! self + SuccessCode === self or error! end @@ -1134,4 +1298,8 @@ module Net } + + HTTPResponse = NetPrivate::HTTPResponse + HTTPResponseReceiver = NetPrivate::HTTPResponse + end # module Net diff --git a/lib/net/pop.rb b/lib/net/pop.rb index c61e09735d..fee68f25a0 100644 --- a/lib/net/pop.rb +++ b/lib/net/pop.rb @@ -1,25 +1,115 @@ =begin -= net/pop.rb version 1.2.0 += net/pop.rb version 1.2.1 - Copyright (C) 1999-2001 Yukihiro Matsumoto +Copyright (c) 1999-2001 Yukihiro Matsumoto - written & maintained by Minero Aoki +written & maintained by Minero Aoki - This program is free software. You can re-distribute and/or - modify this program under the same terms as Ruby itself, - GNU General Public License or Ruby License. +This program is free software. You can re-distribute and/or +modify this program under the same terms as Ruby itself, +Ruby Distribute License or GNU General Public License. - Japanese version of this document is in "net" full package. - You can get it from RAA (Ruby Application Archive). RAA is: - http://www.ruby-lang.org/en/raa.html +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +(()) + +== What is This Module? + +This module provides your program the functions to retrieve +mails via POP3, Post Office Protocol version 3. For details +of POP3, refer [RFC1939] (()). + +== Examples + +=== Retrieving Mails + +This example retrieves mails from server and delete it (on server). +Mails are written in file named 'inbox/1', 'inbox/2', .... +Replace 'pop3.server.address' your POP3 server address. -== Net::POP3 + require 'net/pop' -=== Super Class + Net::POP3.start( 'pop3.server.address', 110, + 'YourAccount', 'YourPassword' ) {|pop| + if pop.mails.empty? then + puts 'no mail.' + else + i = 0 + pop.each_mail do |m| # or "pop.mails.each ..." + File.open( 'inbox/' + i.to_s, 'w' ) do |f| + f.write m.pop + end + m.delete + i += 1 + end + end + puts "#{pop.mails.size} mails popped." + } -Net::Protocol +=== Shorter Version + + require 'net/pop' + i = 0 + Net::POP3.start( 'pop3.server.address', 110, + 'YourAccount', 'YourPassword' ) {|pop| + pop.delete_all do |m| + File.open( 'inbox/' + i.to_s, 'w' ) {|f| + f.write m.pop + } + i += 1 + end + } + +And this is more shorter example. + + require 'net/pop' + i = 0 + Net::POP3.delete_all( 'pop3.server.address', 110, + 'YourAccount', 'YourPassword' ) do |m| + File.open( 'inbox/' + i.to_s, 'w' ) {|f| + f.write m.pop + } + i += 1 + end + +=== Writing to File directly + +All examples above get mail as one big string. +This example does not create such one. + + require 'net/pop' + Net::POP3.delete_all( 'pop3.server.address', 110, + 'YourAccount', 'YourPassword' ) do |m| + File.open( 'inbox', 'w' ) {|f| + m.pop f #### + } + end + +=== Using APOP + +net/pop also supports APOP authentication. There's two way to use APOP: +(1) using APOP class instead of POP3 +(2) passing true for fifth argument of POP3.start + + # (1) + require 'net/pop' + Net::APOP.start( 'apop.server.address', 110, + 'YourAccount', 'YourPassword' ) {|pop| + # Rest code is same. + } + + # (2) + require 'net/pop' + Net::POP3.start( 'apop.server.address', 110, + 'YourAccount', 'YourPassword', + true #### + ) {|pop| + # Rest code is same. + } + +== Net::POP3 class === Class Methods @@ -31,10 +121,9 @@ Net::Protocol : start( address = 'localhost', port = 110, account, password ) {|pop| .... } equals to Net::POP3.new( address, port ).start( account, password ) - # typical usage - Net::POP3.start( addr, port, acnt, pass ) do |pop| + Net::POP3.start( addr, port, account, password ) do |pop| pop.each_mail do |m| - any_file.write m.pop + file.write m.pop m.delete end end @@ -49,9 +138,8 @@ Net::Protocol end end - . + Typical usage: - # typical usage Net::POP3.foreach( addr, nil, acnt, pass ) do |m| m.pop file m.delete @@ -59,120 +147,140 @@ Net::Protocol : delete_all( address = 'localhost', port = 110, account, password ) : delete_all( address = 'localhost', port = 110, account, password ) {|mail| .... } - starts POP3 session and delete all mails. - If block is given, iterates for each POPMail object before delete. + starts POP3 session and delete all mails. + If block is given, iterates for each POPMail object before delete. + + # example + Net::POP3.delete_all( addr, nil, 'YourAccount', 'YourPassword' ) do |m| + m.pop file + end + +: auth_only( address = 'localhost', port = 110, account, password ) + (just for POP-before-SMTP) + opens POP3 session and does autholize and quit. + This method must not be called while POP3 session is opened. + + # example + pop = Net::POP3.auth_only( 'your.pop3.server', + nil, # using default (110) + 'YourAccount', + 'YourPassword' ) - # typical usage - Net::POP3.delete_all( addr, nil, acnt, pass ) do |m| - m.pop file - end - === Instance Methods : start( account, password ) : start( account, password ) {|pop| .... } - starts POP3 session. + starts POP3 session. - When called with block, gives a POP3 object to block and - closes the session after block call finish. + When called with block, gives a POP3 object to block and + closes the session after block call finish. + +: active? + true if POP3 session is started. + +: address + the address to connect + +: port + the port number to connect + +: open_timeout +: open_timeout=(n) + seconds to wait until connection is opened. + If POP3 object cannot open a conection in this seconds, + it raises TimeoutError exception. + +: read_timeout +: read_timeout=(n) + seconds to wait until reading one block (by one read(1) call). + If POP3 object cannot open a conection in this seconds, + it raises TimeoutError exception. + +: finish + finishes POP3 session. + If POP3 session had not be started, does nothing and return false. : mails - an array of ((URL:#POPMail)). - This array is renewed when session started. + an array of Net::POPMail objects. + This array is renewed when session started. : each_mail {|popmail| .... } : each {|popmail| .... } - is equals to "pop3.mails.each" + is equals to "pop3.mails.each" : delete_all : delete_all {|popmail| .... } - deletes all mails. - If called with block, gives mails to the block before deleting. + deletes all mails on server. + If called with block, gives mails to the block before deleting. - # example 1 - # pop and delete all mails - n = 1 - pop.delete_all do |m| - File.open("inbox/#{n}") {|f| f.write m.pop } - n += 1 - end + # example + n = 1 + pop.delete_all do |m| + File.open("inbox/#{n}") {|f| f.write m.pop } + n += 1 + end - # example 2 - # clear all mails on server - Net::POP3.start( addr, port, acc, pass ) do |pop| - pop.delete_all - end +: auth_only( account, password ) + (just for POP-before-SMTP) + opens POP3 session and does autholize and quit. + This method must not be called while POP3 session is opened. + # example + pop = Net::POP3.new( 'your.pop3.server' ) + pop.auth_only 'YourAccount', 'YourPassword' : reset reset the session. All "deleted mark" are removed. - == Net::APOP This class defines no new methods. Only difference from POP3 is using APOP authentification. === Super Class - Net::POP3 - == Net::POPMail A class of mail which exists on POP server. -=== Super Class - -Object - - -=== Methods +=== Instance Methods : pop( dest = '' ) - This method fetches a mail and write to 'dest' using '<<' method. + This method fetches a mail and write to 'dest' using '<<' method. - # usage example - - mailarr = [] - POP3.start( 'localhost', 110 ) do |pop| - pop.each_mail do |popm| - mailarr.push popm.pop # all() returns 'dest' (this time, string) - # or, you can also - # popm.pop( $stdout ) # write mail to stdout - - # maybe you also want to delete mail after popping - popm.delete - end - end + # example + allmails = nil + POP3.start( 'your.pop3.server', 110, + 'YourAccount, 'YourPassword' ) do |pop| + allmails = pop.mails.collect {|popmail| popmail.pop } + end : pop {|str| .... } - If pop() is called with block, it gives the block part strings of a mail. + gives the block part strings of a mail. - # usage example - - POP3.start( 'localhost', 110 ) do |pop3| - pop3.each_mail do |m| - m.pop do |str| - # do anything - end - end - end + # example + POP3.start( 'localhost', 110 ) {|pop3| + pop3.each_mail do |m| + m.pop do |str| + # do anything + end + end + } : header - This method fetches only mail header. + This method fetches only mail header. : top( lines ) - This method fetches mail header and 'lines' lines body. + This method fetches mail header and LINES lines of body. : delete -: delete! - This method deletes mail. + deletes mail on server. : size - size of mail(bytes) + mail size (bytes) : deleted? - true if mail was deleted + true if mail was deleted =end @@ -205,7 +313,12 @@ module Net pop.delete_all( &block ) end end - + + def auth_only( address = nil, port = nil, + account = nil, password = nil ) + new( address, port ).auth_only account, password + end + end @@ -215,6 +328,18 @@ module Net @apop = false end + def auth_only( account = nil, password = nil ) + begin + connect + @active = true + @command.auth address, port + @command.quit + ensure + @active = false + disconnect + end + end + attr :mails def each_mail( &block ) @@ -225,6 +350,7 @@ module Net alias each each_mail def delete_all + io_check @mails.each do |m| yield m if block_given? m.delete unless m.deleted? @@ -247,23 +373,20 @@ module Net (@apop ? type.apop_command_type : type.command_type).new(sock) end - def do_start( acnt, pwd ) - @command.auth( acnt, pwd ) + def do_start( account, password ) + @command.auth account, password - @mails = [] + mails = [] mtype = type.mail_type @command.list.each_with_index do |size,idx| - if size then - @mails.push mtype.new( idx, size, @command ) - end + mails.push mtype.new(idx, size, @command) if size end - @mails.freeze + @mails = mails.freeze end def io_check - if not @socket or @socket.closed? then - raise IOError, 'pop session is not opened yet' - end + (not @socket or @socket.closed?) and + raise IOError, 'pop session is not opened yet' end end @@ -359,8 +482,9 @@ module Net critical { getok 'LIST' @socket.read_pendlist do |line| - num, siz = line.split( / +/o ) - arr[ num.to_i ] = siz.to_i + m = /\A(\d+)[ \t]+(\d+)/.match(line) or + raise BadResponse, "illegal response: #{line}" + arr[ m[1].to_i ] = m[2].to_i end } arr @@ -376,26 +500,26 @@ module Net def top( num, lines = 0, dest = '' ) critical { getok sprintf( 'TOP %d %d', num, lines ) - @socket.read_pendstr( dest ) + @socket.read_pendstr dest } end def retr( num, dest = '', &block ) critical { - getok sprintf( 'RETR %d', num ) - @socket.read_pendstr( dest, &block ) + getok sprintf('RETR %d', num) + @socket.read_pendstr dest, &block } end def dele( num ) critical { - getok sprintf( 'DELE %d', num ) + getok sprintf('DELE %d', num) } end def uidl( num ) critical { - getok( sprintf 'UIDL %d', num ).msg.split(' ')[1] + getok( sprintf('UIDL %d', num) ).msg.split(' ')[1] } end @@ -436,17 +560,16 @@ module Net def initialize( sock ) rep = super( sock ) - m = /<.+>/.match( rep.msg ) - unless m then - raise ProtoAuthError, "This is not APOP server: can't login" - end + m = /<.+>/.match( rep.msg ) or + raise ProtoAuthError, "not APOP server: cannot login" @stamp = m[0] end def auth( account, pass ) critical { @socket.writeline sprintf( 'APOP %s %s', - account, MD5.new(@stamp + pass).hexdigest ) + account, + MD5.new(@stamp + pass).hexdigest ) check_reply_auth } end diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb index a440ef0817..52c4926d43 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -1,62 +1,18 @@ =begin -= net/protocol.rb version 1.2.0 += net/protocol.rb version 1.2.1 - Copyright (C) 1999-2001 Yukihiro Matsumoto +Copyright (c) 1999-2001 Yukihiro Matsumoto - written & maintained by Minero Aoki +written & maintained by Minero Aoki - This program is free software. You can re-distribute and/or - modify this program under the same terms as Ruby itself, - GNU General Public License or Ruby License. +This program is free software. You can re-distribute and/or +modify this program under the same terms as Ruby itself, +Ruby Distribute License or GNU General Public License. - Japanese version of this document is in "net" full package. - You can get it from RAA (Ruby Application Archive). RAA is: - http://www.ruby-lang.org/en/raa.html - - -== Net::Protocol - -the abstract class for some internet protocols - -=== Super Class - -Object - -=== Class Methods - -: new( address = 'localhost', port = nil ) - This method Creates a new protocol object. - -: start( address = 'localhost', port = nil, *protoargs ) -: start( address = 'localhost', port = nil, *protoargs ) {|proto| .... } - This method creates a new Protocol object and opens a session. - equals to Net::Protocol.new( address, port ).start( *protoargs ) - -=== Methods - -: address - the address of connecting server (FQDN). - -: port - connecting port number - -: start( *args ) -: start( *args ) {|proto| .... } - This method starts protocol. If protocol was already started, - do nothing and returns false. - - '*args' are specified in subclasses. - - When is called with block, gives Protocol object to block and - close session when block finished. - -: finish - This method ends protocol. If you call this method before protocol starts, - it only return false without doing anything. - -: active? - true if session have been started +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +(()) =end @@ -76,8 +32,7 @@ module Net class Protocol - Version = '1.2.0' - + Version = '1.2.1' class << self @@ -106,13 +61,17 @@ module Net # - # sub-class requirements + # --- Configuration Staffs for Sub Classes --- # - # protocol_param command_type - # protocol_param port + # protocol_param port + # protocol_param command_type + # protocol_param socket_type (optional) # - # private method do_start (optional) - # private method do_finish (optional) + # private method do_start (optional) + # private method do_finish (optional) + # + # private method on_connect (optional) + # private method on_disconnect (optional) # protocol_param :port, 'nil' @@ -267,8 +226,8 @@ module Net "#<#{type} #{code}>" end - def error!( data = nil ) - raise code_type.error_type.new( code + ' ' + Net.quote(msg), data ) + def error! + raise code_type.error_type.new( code + ' ' + Net.quote(msg), self ) end end @@ -288,12 +247,13 @@ module Net class ProtocolError - def initialize( msg, data = nil ) + def initialize( msg, resp ) super msg - @data = data + @response = resp end - attr :data + attr :response + alias data response def inspect "#<#{type}>" @@ -644,7 +604,7 @@ module Net # private use only (can not handle 'break') def read_pendlist - D_off 'reading list...' + # D_off 'reading list...' str = nil i = 0 @@ -654,7 +614,7 @@ module Net yield str end - D_on "read #{i} items" + # D_on "read #{i} items" end diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb index 3b1d100fb3..52402a46f6 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -1,86 +1,193 @@ =begin -= net/smtp.rb version 1.2.0 += net/smtp.rb version 1.2.1 - Copyright (C) 1999-2001 Yukihiro Matsumoto +Copyright (c) 1999-2001 Yukihiro Matsumoto - written & maintained by Minero Aoki +written & maintained by Minero Aoki - This program is free software. You can re-distribute and/or - modify this program under the same terms as Ruby itself, - GNU General Public License or Ruby License. +This program is free software. You can re-distribute and/or +modify this program under the same terms as Ruby itself, +Ruby Distribute License or GNU General Public License. - Japanese version of this document is in "net" full package. - You can get it from RAA (Ruby Application Archive). RAA is: - http://www.ruby-lang.org/en/raa.html +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +(()) +== What is This Module? -== Net::SMTP +This module provides your program the functions to send internet +mail via SMTP, Simple Mail Transfer Protocol. For details of +SMTP itself, refer [RFC2821] (()). -=== Super Class +== What This Module is NOT? -Net::Protocol +This module does NOT provide the functions to compose internet +mail. You must create it by yourself. For details of internet mail +format, see [RFC2822] (()). + +== Examples + +=== Sending Mail + +You must open connection to SMTP server before sending mails. +First argument is the address of SMTP server, and second argument +is port number. Using SMTP.start with block is the most simple way +to do it. SMTP Connection is closed automatically after block is +executed. + + require 'net/smtp' + Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp| + # use smtp object only in this block + } + +Replace 'your.smtp.server' by your SMTP server. Normally +your system manager or internet provider is supplying a server +for you. + +Then you can send mail. + + require 'net/smtp' + + Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp| + smtp.send_mail < + To: Dest Address + Subject: test mail + Date: Sat, 23 Jun 2001 16:26:43 +0900 + Message-Id: + + This is test mail. + EndOfMail + } + +=== Sending Mails from Any Sources + +In an example above I sent mail from String (here document literal). +SMTP#send_mail accepts any objects which has "each" method +like File and Array. + + require 'net/smtp' + Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp| + File.open( 'Mail/draft/1' ) {|f| + smtp.send_mail f, 'your@mail.address', 'to@some.domain' + } + } + +=== Giving "Hello" Domain + +If your machine does not have canonical host name, maybe you +must designate the third argument of SMTP.start. + + Net::SMTP.start( 'your.smtp.server', 25, + 'mail.from.domain' ) {|smtp| + +This argument gives MAILFROM domain, the domain name that +you send mail from. SMTP server might judge if he (or she?) +send or reject SMTP session by this data. + +== class Net::SMTP === Class Methods : new( address = 'localhost', port = 25 ) creates a new Net::SMTP object. -: start( address = 'localhost', port = 25, *protoargs ) -: start( address = 'localhost', port = 25, *protoargs ) {|smtp| .... } - is equal to Net::SMTP.new( address, port ).start( *protoargs ) +: start( address = 'localhost', port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) +: start( address = 'localhost', port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... } + is equal to + Net::SMTP.new(address,port).start(helo_domain,account,password,authtype) -=== Methods + # example + Net::SMTP.start( 'your.smtp.server' ) { + smtp.send_mail mail_string, 'from@mail.address', 'dest@mail.address' + } -: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) -: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... } +=== Instance Methods + +: start( helo_domain = , account = nil, password = nil, authtype = nil ) +: start( helo_domain = , account = nil, password = nil, authtype = nil ) {|smtp| .... } opens TCP connection and starts SMTP session. If protocol had been started, do nothing and return false. + HELO_DOMAIN is a domain that you'll dispatch mails from. When this methods is called with block, give a SMTP object to block and close session after block call finished. - If account and password are given, is trying to get authentication - by using AUTH command. "authtype" is :plain (symbol) or :cram_md5. + If both of account and password are given, is trying to get + authentication by using AUTH command. :plain or :cram_md5 is + allowed for AUTHTYPE. + +: active? + true if SMTP session is started. + +: address + the address to connect + +: port + the port number to connect + +: open_timeout +: open_timeout=(n) + seconds to wait until connection is opened. + If SMTP object cannot open a conection in this seconds, + it raises TimeoutError exception. + +: read_timeout +: read_timeout=(n) + seconds to wait until reading one block (by one read(1) call). + If SMTP object cannot open a conection in this seconds, + it raises TimeoutError exception. + +: finish + finishes SMTP session. + If SMTP session had not started, do nothing and return false. : send_mail( mailsrc, from_addr, *to_addrs ) -: sendmail( mailsrc, from_addr, *to_addrs ) - This method sends 'mailsrc' as mail. SMTP read strings - from 'mailsrc' by calling 'each' iterator, and convert them - into "\r\n" terminated string when write. + This method sends MAILSRC as mail. A SMTP object read strings + from MAILSRC by calling "each" iterator, with converting them + into CRLF ("\r\n") terminated string when write. - from_addr must be String. - to_addrs must be a String(s) or an Array of String. + FROM_ADDR must be a String, representing source mail address. + TO_ADDRS must be Strings or an Array of Strings, representing + destination mail addresses. - Exceptions which SMTP raises are: - * Net::ProtoSyntaxError: syntax error (errno.500) - * Net::ProtoFatalError: fatal error (errno.550) - * Net::ProtoUnknownError: unknown error - * Net::ProtoServerBusy: temporary error (errno.420/450) + # example + Net::SMTP.start( 'your.smtp.server' ) {|smtp| + smtp.send_mail mail_string, + 'from@mail.address', + 'dest@mail.address' 'dest2@mail.address' + } - # usage example +: ready( from_addr, *to_addrs ) {|adapter| .... } + This method stands by the SMTP object for sending mail and + give adapter object to the block. ADAPTER accepts only "write" + method. - Net::SMTP.start( 'localhost', 25 ) do |smtp| - smtp.send_mail mail_string, 'from-addr@foo.or.jp', 'to-addr@bar.or.jp' - end + FROM_ADDR must be a String, representing source mail address. + TO_ADDRS must be Strings or an Array of Strings, representing + destination mail addresses. -: ready( from_addr, to_addrs ) {|adapter| .... } - This method stands by the SMTP object for sending mail. - "adapter" object accepts only "write" method. - - # usage example - - Net::SMTP.start( 'localhost', 25 ) do |smtp| - smtp.ready( from, to ) do |adapter| + # example + Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp| + smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) do |adapter| adapter.write str1 adapter.write str2 adapter.write str3 end - end + } -: finish - finishes SMTP session. - If SMTP session had not started, do nothing and return false. +== Exceptions + +SMTP objects raise these exceptions: +: Net::ProtoSyntaxError + syntax error (errno.500) +: Net::ProtoFatalError + fatal error (errno.550) +: Net::ProtoUnknownError + unknown error. (is probably bug) +: Net::ProtoServerBusy + temporary error (errno.420/450) =end @@ -155,7 +262,7 @@ module Net if user or secret then (user and secret) or - raise ArgumentError, "both of account and password are required" + raise ArgumentError, 'both of account and password are required' mid = 'auth_' + (authtype || 'cram_md5').to_s @command.respond_to? mid or