diff options
author | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2001-06-26 23:49:21 +0000 |
---|---|---|
committer | aamine <aamine@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2001-06-26 23:49:21 +0000 |
commit | 90030b920bc3eb36f34dda1d6865a1f0c406799b (patch) | |
tree | 9d92fcbe4abb12a6a149d08ce23f6975350deabd /lib/net | |
parent | 5c806765de6b0c679d132f80dc4189c77e1b772c (diff) |
aamine
* 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.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_6@1546 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/net')
-rw-r--r-- | lib/net/http.rb | 596 | ||||
-rw-r--r-- | lib/net/pop.rb | 341 | ||||
-rw-r--r-- | lib/net/protocol.rb | 92 | ||||
-rw-r--r-- | lib/net/smtp.rb | 203 |
4 files changed, 767 insertions, 465 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb index 26e5285525..cc0d400bb0 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1,267 +1,389 @@ =begin -= net/http.rb version 1.1.34 += net/http.rb version 1.1.35 -written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> +Copyright (c) 1999-2001 Yukihiro Matsumoto +written & maintained by Minero Aoki <aamine@loveruby.net> This file is derived from "http-access.rb". -This program is free software. -You can distribute/modify this program under -the terms of the Ruby Distribute 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: http://www.ruby-lang.org/en/raa.html). +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +((<URL:http://www.ruby-lang.org/~rubikitch/RDP.cgi>)) +== What is this module? -== http.rb version 1.2 features +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] +((<URL:http://www.ietf.org/rfc/rfc2616.txt>)). -You can use 1.2 features by calling HTTP.version_1_2. And -calling Net::HTTP.version_1_1 allows to use 1.1 features. +== Examples - # example - HTTP.start {|http1| ...(http1 has 1.1 features)... } +=== Getting Document From Server - HTTP.version_1_2 - HTTP.start {|http2| ...(http2 has 1.2 features)... } +Be care to ',' (comma) putted after "response". +This is required for feature compatibility. - HTTP.version_1_1 - HTTP.start {|http3| ...(http3 has 1.1 features)... } + 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 -Changes are: + 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 - * HTTP#get, head, post does not raise ProtocolError - * HTTP#get, head, post returns only one object, a HTTPResponse object - * HTTPResponseReceiver is joined into HTTPResponse - * request object: HTTP::Get, Head, Post; and HTTP#request(req) +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. -WARNING: These features are not definite yet. -They will change without notice! + 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<http:([^/]+)>.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)... } -== class HTTP + 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 - Net::HTTP.new( address, port, proxy_addr, proxy_port ).start(&block) + is equals to -: Proxy( address, port ) - creates a HTTP proxy class. - Arguments are address/port of proxy host. - You can replace HTTP class by this proxy class. + Net::HTTP.new(address, port, proxy_addr, proxy_port).start(&block) - # example - proxy_http = HTTP::Proxy( 'proxy.foo.org', 8080 ) - : - proxy_http.start( 'www.ruby-lang.org' ) do |http| - # connecting proxy.foo.org:8080 - : - end +: get( address, path, port = 80 ) + 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). + +: 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. + + 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). -=== Methods +=== 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 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 - When this method is called with block, gives a HTTP object to block - and close HTTP session after returning from the block. +: 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| .... } - get data from "path" on connecting host. - "header" must be a Hash like { 'Accept' => '*/*', ... }. - Data is written to "dest" by using "<<" method. - This method returns HTTPResponse object, and "dest". + 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, body = 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. - Note: - If status is not 2xx(success), ProtocolError exception is - raised. At that time, you can get HTTPResponse object from - exception object. (same in head/post) + In version 1.2, this method never raises exception. - # example - begin - response, body = http.get( '/index.html' ) - rescue Net::ProtoRetriableError => err - response = err.data - ... - end + # version 1.1 (Ruby 1.6) + response, body = http.get( '/index.html' ) -: head( path, header = nil ) - gets only header from "path" on connecting host. - "header" is a Hash like { 'Accept' => '*/*', ... }. - This method returns a HTTPResponse object. - You can http header from this object like: + # version 1.2 (Ruby 1.7 or later) + response = http.get( '/index.html' ) - response['content-length'] #-> '2554' - response['content-type'] #-> 'text/html' - response['Content-Type'] #-> 'text/html' - response['CoNtEnT-tYpe'] #-> 'text/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: + + 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 now) to "path". - If the body exists, also gets entity body. - Data is written to "dest" by using "<<" method. - "header" must be a Hash like { 'Accept' => '*/*', ... }. - This method returns HTTPResponse object and "dest". + 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. + + # 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' ) + + # 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 + } - If called with block, gives a part of entity body string. +: request( request, [data] ) +: request( request, [src] ) {|response| .... } + 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.) -: get2( path, header = nil ) -: get2( path, header = nil ) {|recv| .... } - send GET request for "path". - "header" must be a Hash like { 'Accept' => '*/*', ... }. - If this method is called with block, one gives - a HTTPResponseReceiver object to block. + If called with block, gives a HTTPResponse object to the block. - # example - http.get2( '/index.html' ) do |recv| - # "recv" is a HTTPResponseReceiver object - recv.header - recv.body - end +== class Net::HTTP::Get, Head, Post - # another way - response = http.get2( '/index.html' ) - response['content-type'] - response.body +HTTP request classes. These classes wraps request header and +entity path. All arguments named "key" is case-insensitive. - # this is wrong - http.get2( '/index.html' ) do |recv| - print recv.response.body # body is not read yet!!! - end +=== Class Methods - # but this is ok - http.get2( '/index.html' ) do |recv| - recv.body # read body and set recv.response.body - print recv.response.body # ref - end +: new + creats HTTP request object. -: head2( path, header = nil ) -: head2( path, header = nil ) {|recv| .... } - send HEAD request for "path". - "header" must be a Hash like { 'Accept' => 'text/html', ... }. - The difference between "head" method is that - "head2" does not raise exceptions. +=== Instance Methods - If this method is called with block, one gives - a HTTPResponseReceiver object to block. +: self[ key ] + returns the header field corresponding to the case-insensitive key. + For example, a key of "Content-Type" might return "text/html" - # example - response = http.head2( '/index.html' ) +: self[ key ] = val + sets the header field corresponding to the case-insensitive key. - # another way - http.head2( '/index.html' ) do |recv| - recv.response - end +: each {|name, val| .... } + iterates for each field name and value pair. -: post2( path, data, header = nil ) -: post2( path, data, header = nil ) {|recv| .... } - posts "data" (must be String now) to "path". - "header" must be a Hash like { 'Accept' => '*/*', ... }. - If this method is called with block, one gives - a HTTPResponseReceiver object to block. +: basic_auth( account, password ) + set Authorization: header for basic auth. - # example - http.post2( '/anycgi.rb', 'data data data...' ) do |recv| - # "recv" is a HTTPResponseReceiver object - recv.header - recv.body - end +: range + returns a Range object which represents Range: header field. - # another way - response = http.post2( '/anycgi.rb', 'important data' ) - response['content-type'] - response.body +: 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. -== class HTTPResponse +: content_range + returns a Range object which represents Content-Range: header field. -HTTP response object. -All "key" is case-insensitive. +== class Net::HTTPResponse -=== Methods +HTTP response class. This class wraps response header and entity. +All arguments named KEY is case-insensitive. -: body - the entity body (String). +=== Instance Methods : self[ key ] - returns header field for "key". - for HTTP, value is a string like 'text/plain'(for Content-Type), - '2045'(for Content-Length), 'bytes 0-1023/10024'(for Content-Range). - If there's some fields which has same name, they are joined with ','. + returns the header field corresponding to the case-insensitive key. + For example, a key of "Content-Type" might return "text/html". + A key of "Content-Length" might do "2045". + + More than one fields which has same names are joined with ','. : self[ key ] = val - set field value for "key". + sets the header field corresponding to the case-insensitive key. : key?( key ) - true if key exists + true if key exists. + KEY is case insensitive. : each {|name,value| .... } - iterates for each field name and value pair. + iterates for each field name and value pair. + +: canonical_each {|name,value| .... } + iterates for each "canonical" field name and value pair. : code - HTTP result code string. For example, '302'. + HTTP result code string. For example, '302'. : message - HTTP result message. For example, 'Not Found'. - - -== class HTTPResponseReceiver - -=== Methods - -: header -: response - HTTPResponse object + HTTP result message. For example, 'Not Found'. : read_body( dest = '' ) - reads entity body into DEST by calling "<<" method and - returns DEST. + 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 {|string| ... } - reads entity body little by little and gives it to block - until entity ends. +: read_body {|str| .... } + gets entity body little by little and pass it to block. : body -: entity - entity body. If #read_body is called already, returns its - argument DEST. Else returns entity body as String. + response body. If #read_body has been called, this method returns + arg of #read_body DEST. Else gets body as String and returns it. - Calling this method any times causes returning same - object (does not read entity again). =end @@ -284,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 ) @@ -315,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 @@ -331,32 +452,34 @@ module Net new( address, port, p_addr, p_port ).start( &block ) end - def proxy_class? - false - end + @is_proxy_class = false + @proxy_addr = nil + @proxy_port = nil - def proxy_address - nil + def proxy_class? + @is_proxy_class 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 @@ -364,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 - - end - - def initialize( addr, port ) - super - @proxy_address = type.proxy_address - @proxy_port = type.proxy_port + 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 - - 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 ) @@ -423,7 +516,11 @@ module Net # for backward compatibility # - @@newimpl = false + if Version < '1.2.0' then ###noupdate + @@newimpl = false + else + @@newimpl = true + end class << self @@ -435,6 +532,10 @@ module Net @@newimpl = false end + def is_version_1_2? + @@newimpl + end + private def setvar( obj ) @@ -564,7 +665,10 @@ module Net end def self.get_print( addr, path, port = nil ) - print get( addr, path, port ) + new( addr, port || HTTP.port ).start {|http| + http.get path, nil, $stdout + } + nil end @@ -719,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 ) @@ -793,8 +897,7 @@ module Net end def basic_auth( acc, pass ) - @header['authorization'] = - 'Basic ' + ["#{acc}:#{pass}"].pack('m').gsub(/\s+/, '') + @header['authorization'] = 'Basic ' + ["#{acc}:#{pass}"].pack('m').strip end end @@ -964,7 +1067,10 @@ module Net @buf.concat s end - alias << write + def <<( s ) + @buf.concat s + self + end def terminate ret = @buf @@ -1086,7 +1192,7 @@ module Net end def value - SuccessCode === self or error! self + SuccessCode === self or error! end @@ -1094,12 +1200,12 @@ module Net # header (for backward compatibility) # - def read_header + def response self end - alias header read_header - alias response read_header + alias header response + alias read_header response # # body diff --git a/lib/net/pop.rb b/lib/net/pop.rb index 8f3f978e8c..6812b94c36 100644 --- a/lib/net/pop.rb +++ b/lib/net/pop.rb @@ -1,27 +1,119 @@ =begin -= net/pop.rb version 1.1.34 += net/pop.rb version 1.1.35 -written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> +Copyright (c) 1999-2001 Yukihiro Matsumoto -This program is free software. -You can distribute/modify this program under -the terms of the Ruby Distribute License. +written & maintained by Minero Aoki <aamine@loveruby.net> -Japanese version of this document is in "net" full package. -You can get it from RAA -(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html). +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. +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +((<URL:http://www.ruby-lang.org/~rubikitch/RDP.cgi>)) -== Net::POP3 +== What is This Module? -=== Super Class +This module provides your program the functions to retrieve +mails via POP3, Post Office Protocol version 3. For details +of POP3, refer [RFC1939] ((<URL:http://www.ietf.org/rfc/rfc1939.txt>)). + +== 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. + + + require 'net/pop' + + 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." + } + +=== 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::Protocol +== Net::POP3 class === Class Methods -: new( address = 'localhost', port = 110 ) +: new( address = 'localhost', port = 110, apop = false ) creates a new Net::POP3 object. This method does not open TCP connection yet. @@ -29,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 @@ -47,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 @@ -57,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. - # typical usage - Net::POP3.delete_all( addr, nil, acnt, pass ) do |m| - m.pop file - end - -=== Methods + # 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' ) + +=== 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. + +: active? + true if POP3 session is started. + +: address + the address to connect - When called with block, gives a POP3 object to block and - closes the session after block call finish. +: 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. - - # 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 + deletes all mails on server. + If called with block, gives mails to the block before deleting. + + # 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. - - # usage example + This method fetches a mail and write to 'dest' using '<<' method. - 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 @@ -203,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 @@ -213,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 ) @@ -223,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? @@ -245,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 @@ -357,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 @@ -374,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 @@ -434,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 343721add3..28ffab5c72 100644 --- a/lib/net/protocol.rb +++ b/lib/net/protocol.rb @@ -1,60 +1,18 @@ =begin -= net/protocol.rb version 1.1.34 += net/protocol.rb version 1.1.35 -written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> +Copyright (c) 1999-2001 Yukihiro Matsumoto -This program is free software. -You can distribute/modify this program under -the terms of the Ruby Distribute License. +written & maintained by Minero Aoki <aamine@loveruby.net> -Japanese version of this document is in "net" full package. -You can get it from RAA -(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html). +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. - -== Net::Protocol - -the abstract class for Internet protocol - -=== 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): +((<URL:http://www.ruby-lang.org/~rubikitch/RDP.cgi>)) =end @@ -74,8 +32,7 @@ module Net class Protocol - Version = '1.1.34' - + Version = '1.1.35' class << self @@ -104,13 +61,17 @@ module Net # - # sub-class requirements + # --- Configuration Staffs for Sub Classes --- + # + # protocol_param port + # protocol_param command_type + # protocol_param socket_type (optional) # - # protocol_param command_type - # protocol_param port + # 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' @@ -265,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 @@ -286,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}>" @@ -642,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 @@ -652,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 befc1adf03..4bfae6326f 100644 --- a/lib/net/smtp.rb +++ b/lib/net/smtp.rb @@ -1,84 +1,193 @@ =begin -= net/smtp.rb version 1.1.34 += net/smtp.rb version 1.1.35 -written by Minero Aoki <aamine@dp.u-netsurf.ne.jp> +Copyright (c) 1999-2001 Yukihiro Matsumoto -This program is free software. -You can distribute/modify this program under -the terms of the Ruby Distribute License. +written & maintained by Minero Aoki <aamine@loveruby.net> -Japanese version of this document is in "net" full package. -You can get it from RAA -(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html). +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. +NOTE: You can get Japanese version of this document from +Ruby Documentation Project (RDP): +((<URL:http://www.ruby-lang.org/~rubikitch/RDP.cgi>)) -== Net::SMTP +== What is This Module? -=== Super Class +This module provides your program the functions to send internet +mail via SMTP, Simple Mail Transfer Protocol. For details of +SMTP itself, refer [RFC2821] ((<URL:http://www.ietf.org/rfc/rfc2821.txt>)). -Net::Protocol +== What This Module is NOT? + +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] ((<URL:http://www.ietf.org/rfc/rfc2822.txt>)). + +== 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 <<EndOfMail, 'your@mail.address', 'to@some.domain' + From: Your Name <your@mail.address> + To: Dest Address <to@some.domain> + Subject: test mail + Date: Sat, 23 Jun 2001 16:26:43 +0900 + Message-Id: <unique.message.id.string@some.domain> + + 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 = <local host name>, account = nil, password = nil, authtype = nil ) +: start( helo_domain = <local host name>, 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. -: 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. +: active? + true if SMTP session is started. - from_addr must be String. - to_addrs must be a String(s) or an Array of String. +: address + the address to connect - 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) +: port + the port number to connect - # usage example +: 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. - Net::SMTP.start( 'localhost', 25 ) do |smtp| - smtp.send_mail mail_string, 'from-addr@foo.or.jp', 'to-addr@bar.or.jp' - end +: 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. -: ready( from_addr, to_addrs ) {|adapter| .... } - This method stands by the SMTP object for sending mail. - "adapter" object accepts only "write" method. - - # usage example +: finish + finishes SMTP session. + If SMTP session had not started, do nothing and return false. - Net::SMTP.start( 'localhost', 25 ) do |smtp| - smtp.ready( from, to ) do |adapter| +: send_mail( mailsrc, from_addr, *to_addrs ) + 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 a String, representing source mail address. + TO_ADDRS must be Strings or an Array of Strings, representing + destination mail addresses. + + # example + Net::SMTP.start( 'your.smtp.server' ) {|smtp| + smtp.send_mail mail_string, + 'from@mail.address', + 'dest@mail.address' 'dest2@mail.address' + } + +: 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. + + FROM_ADDR must be a String, representing source mail address. + TO_ADDRS must be Strings or an Array of Strings, representing + destination mail addresses. + + # 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 |