summaryrefslogtreecommitdiff
path: root/lib/net
diff options
context:
space:
mode:
Diffstat (limited to 'lib/net')
-rw-r--r--lib/net/http.rb1118
-rw-r--r--lib/net/imap.rb487
-rw-r--r--lib/net/pop.rb13
-rw-r--r--lib/net/protocol.rb309
-rw-r--r--lib/net/smtp.rb29
-rw-r--r--lib/net/telnet.rb217
6 files changed, 1355 insertions, 818 deletions
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 3900ed6c68..26e5285525 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1,8 +1,9 @@
=begin
-= net/http.rb version 1.1.32
+= net/http.rb version 1.1.34
+
+written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
-maintained by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
This file is derived from "http-access.rb".
This program is free software.
@@ -14,9 +15,34 @@ You can get it from RAA
(Ruby Application Archive: http://www.ruby-lang.org/en/raa.html).
-= class HTTP
+== http.rb version 1.2 features
+
+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.
+
+ # example
+ HTTP.start {|http1| ...(http1 has 1.1 features)... }
+
+ HTTP.version_1_2
+ HTTP.start {|http2| ...(http2 has 1.2 features)... }
+
+ HTTP.version_1_1
+ HTTP.start {|http3| ...(http3 has 1.1 features)... }
+
+Changes are:
-== Class Methods
+ * 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)
+
+WARNING: These features are not definite yet.
+They will change without notice!
+
+
+== class HTTP
+
+=== Class Methods
: new( address = 'localhost', port = 80, proxy_addr = nil, proxy_port = nil )
creates a new Net::HTTP object.
@@ -49,7 +75,7 @@ You can get it from RAA
HTTP default port (80).
-== Methods
+=== Methods
: start
: start {|http| .... }
@@ -72,7 +98,7 @@ You can get it from RAA
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 Net::HTTPResponse object, and "dest".
+ This method returns HTTPResponse object, and "dest".
# example
response, body = http.get( '/index.html' )
@@ -95,7 +121,7 @@ You can get it from RAA
: 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.
+ This method returns a HTTPResponse object.
You can http header from this object like:
response['content-length'] #-> '2554'
@@ -109,7 +135,7 @@ You can get it from RAA
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 Net::HTTPResponse object and "dest".
+ This method returns HTTPResponse object and "dest".
If called with block, gives a part of entity body string.
@@ -181,21 +207,21 @@ You can get it from RAA
response.body
-= class HTTPResponse
+== class HTTPResponse
HTTP response object.
All "key" is case-insensitive.
-== Methods
+=== Methods
: body
- the entity body. ("dest" argument for HTTP#get, post, put)
+ the entity body (String).
: 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-1024/10024'(for Content-Range).
- Multiple header had be joined by HTTP1.1 scheme.
+ '2045'(for Content-Length), 'bytes 0-1023/10024'(for Content-Range).
+ If there's some fields which has same name, they are joined with ','.
: self[ key ] = val
set field value for "key".
@@ -204,88 +230,92 @@ All "key" is case-insensitive.
true if key exists
: each {|name,value| .... }
- iterates for each field name and value pair
+ iterates for each 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'
+ HTTP result message. For example, 'Not Found'.
-= class HTTPResponseReceiver
+== class HTTPResponseReceiver
-== Methods
+=== Methods
: header
: response
- Net::HTTPResponse object
+ HTTPResponse object
-: body( dest = '' )
-: entity( dest = '' )
- entity body. A body is written to "dest" using "<<" method.
+: read_body( dest = '' )
+ reads entity body into DEST by calling "<<" method and
+ returns DEST.
-: body {|str| ... }
- gets entity body with block.
- If this method is called twice, block is not executed and
- returns first "dest".
+: read_body {|string| ... }
+ reads entity body little by little and gives it to block
+ until entity ends.
+: body
+: entity
+ entity body. If #read_body is called already, returns its
+ argument DEST. Else returns entity body as String.
-= http.rb version 1.2 features
+ Calling this method any times causes returning same
+ object (does not read entity again).
-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.
+=end
- # example
- HTTP.start {|http1| ...(http1 has 1.1 features)... }
+require 'net/protocol'
- HTTP.version_1_2
- HTTP.start {|http2| ...(http2 has 1.2 features)... }
- HTTP.version_1_1
- HTTP.start {|http3| ...(http3 has 1.1 features)... }
+module Net
-== Method (only diff to 1.1)
+ class HTTPBadResponse < StandardError; end
+ class HTTPHeaderSyntaxError < StandardError; end
-: get( path, u_header = nil )
-: get( path, u_header = nil ) {|str| .... }
- gets document from "path".
- returns HTTPResponse object.
-: head( path, u_header = nil )
- gets only document header from "path".
- returns HTTPResponse object.
+ class HTTP < Protocol
-: post( path, data, u_header = nil )
-: post( path, data, u_header = nil ) {|str| .... }
- posts "data" to "path" entity and gets document.
- returns HTTPResponse object.
+ HTTPVersion = '1.1'
-=end
+ #
+ # connection
+ #
-require 'net/protocol'
+ protocol_param :port, '80'
-module Net
+ def initialize( addr = nil, port = nil )
+ super
- class HTTPBadResponse < StandardError; end
+ @proxy_address = nil
+ @proxy_port = nil
+ @curr_http_version = HTTPVersion
+ @seems_1_0_server = false
+ end
- class HTTP < Protocol
- protocol_param :port, '80'
- protocol_param :command_type, '::Net::NetPrivate::HTTPCommand'
+ private
+ def conn_command( sock )
+ end
+
+ def do_finish
+ end
+
+
+ #
+ # proxy
+ #
+
+ public
- ###
- ### proxy
- ###
class << self
def Proxy( p_addr, p_port = nil )
- ::Net::NetPrivate::HTTPProxy.create_proxy_class(
- p_addr, p_port || self.port )
+ ProxyMod.create_proxy_class( p_addr, p_port || self.port )
end
alias orig_new new
@@ -293,7 +323,7 @@ module Net
def new( address = nil, port = nil, p_addr = nil, p_port = nil )
c = p_addr ? self::Proxy(p_addr, p_port) : self
i = c.orig_new( address, port )
- setimplv i
+ setvar i
i
end
@@ -332,385 +362,230 @@ module Net
end
- ###
- ### 1.2 implementation
- ###
+ module ProxyMod
- @@newimpl = false
+ class << self
- #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 self.version_1_2
- @@newimpl = true
- end
+ def klass.proxy_address
+ @proxy_address
+ end
- def self.version_1_1
- @@newimpl = false
- end
+ def klass.proxy_port
+ @proxy_port
+ end
- #private
+ klass
+ end
- def self.setimplv( obj )
- f = @@newimpl
- obj.instance_eval { @newimpl = f }
end
- #end
-
-
- ###
- ### http operations
- ###
-
- def get( path, u_header = nil, dest = nil, &block )
- resp = get2( path, u_header ) {|f| f.body( dest, &block ) }
- if @newimpl then
- resp
- else
- resp.value
- return resp, resp.body
+ def initialize( addr, port )
+ super
+ @proxy_address = type.proxy_address
+ @proxy_port = type.proxy_port
end
- end
-
- def get2( path, u_header = nil, &block )
- common_oper( u_header, true, block ) {|uh|
- @command.get edit_path(path), uh
- }
- end
+
+ attr_reader :proxy_address, :proxy_port
+ alias proxyaddr proxy_address
+ alias proxyport proxy_port
- def head( path, u_header = nil )
- resp = head2( path, u_header )
- unless @newimpl then
- resp.value
+ def proxy?
+ true
end
- resp
- end
-
- def head2( path, u_header = nil, &block )
- common_oper( u_header, false, block ) {|uh|
- @command.head edit_path(path), uh
- }
- end
-
-
- def post( path, data, u_header = nil, dest = nil, &block )
- resp = post2( path, data, u_header ) {|f| f.body( dest, &block ) }
- if @newimpl then
- resp
- else
- resp.value
- return resp, resp.body
+
+ private
+
+ def conn_socket( addr, port )
+ super @proxy_address, @proxy_port
end
- end
-
- def post2( path, data, u_header = nil, &block )
- common_oper( u_header, true, block ) {|uh|
- @command.post edit_path(path), uh, data
- }
- end
-
- # not tested because I could not setup apache (__;;;
- def put( path, src, u_header = nil )
- resp = put2( path, src, u_header ) {|f| f.body }
- if @newimpl then
- resp
- else
- resp.value
- return resp, resp.body
+ def edit_path( path )
+ 'http://' + addr_port + path
end
- end
+
+ end # module ProxyMod
- def put2( path, src, u_header = nil, &block )
- common_oper( u_header, true, block ) {|uh|
- @command.put path, uh, src
- }
- end
-
-
- private
-
-
- def common_oper( u_header, body_exist, block )
- header = procheader( u_header )
- recv = err = nil
-
- connecting( header ) {
- recv = HTTPResponseReceiver.new( @command, body_exist )
- yield header
- begin
- block.call recv if block
- rescue Exception => err
- ;
- end
- recv.terminate
-
- recv.response
- }
- raise err if err
- recv.response
- end
+ #
+ # for backward compatibility
+ #
- def connecting( header )
- if not @socket then
- header['Connection'] = 'close'
- start
- elsif @socket.closed? then
- @socket.reopen
- end
+ @@newimpl = false
- resp = yield
+ class << self
- unless keep_alive? header, resp then
- @socket.close
+ def version_1_2
+ @@newimpl = true
end
- end
- def keep_alive?( header, resp )
- if resp.key? 'connection' then
- if /keep-alive/i === resp['connection'] then
- return true
- end
- elsif resp.key? 'proxy-connection' then
- if /keep-alive/i === resp['proxy-connection'] then
- return true
- end
- elsif header.key? 'Connection' then
- if /keep-alive/i === header['Connection'] then
- return true
- end
- else
- if @command.http_version == '1.1' then
- return true
- end
+ def version_1_1
+ @@newimpl = false
end
- false
- end
-
- def procheader( h )
- ret = {}
- ret[ 'Host' ] = address +
- ((port == HTTP.port) ? '' : ":#{port}")
- ret[ 'Connection' ] = 'Keep-Alive'
- ret[ 'Accept' ] = '*/*'
+ private
- return ret unless h
- tmp = {}
- h.each do |k,v|
- key = k.split('-').collect {|i| i.capitalize }.join('-')
- if tmp[key] then
- $stderr.puts "'#{key}' http header appered twice" if $VERBOSE
- end
- tmp[key] = v
+ def setvar( obj )
+ f = @@newimpl
+ obj.instance_eval { @newimpl = f }
end
- ret.update tmp
- ret
end
- def do_finish
- end
+ #
+ # http operations
+ #
- end
-
- HTTPSession = HTTP
-
-
- module NetPrivate
-
- module HTTPProxy
-
- class << self
-
- def create_proxy_class( p_addr, p_port )
- klass = Class.new( HTTP )
- klass.module_eval {
- include HTTPProxy
- @proxy_address = p_addr
- @proxy_port = p_port
- }
- def klass.proxy_class?
- true
- end
+ public
- def klass.proxy_address
- @proxy_address
+ def self.def_http_method( nm, hasdest, hasdata )
+ name = nm.id2name.downcase
+ cname = nm.id2name
+ lineno = __LINE__ + 2
+ src = <<" ----"
+
+ def #{name}( path, #{hasdata ? 'data,' : ''}
+ u_header = nil #{hasdest ? ',dest = nil, &block' : ''} )
+ resp = nil
+ request(
+ #{cname}.new( path, u_header ) #{hasdata ? ',data' : ''}
+ ) do |resp|
+ resp.read_body( #{hasdest ? 'dest, &block' : ''} )
+ end
+ if @newimpl then
+ resp
+ else
+ resp.value
+ #{hasdest ? 'return resp, resp.body' : 'resp'}
+ end
end
- def klass.proxy_port
- @proxy_port
+ def #{name}2( path, #{hasdata ? 'data,' : ''}
+ u_header = nil, &block )
+ request( #{cname}.new(path, u_header),
+ #{hasdata ? 'data,' : ''} &block )
end
-
- klass
- end
-
- end
-
-
- def initialize( addr, port )
- super
- @proxy_address = type.proxy_address
- @proxy_port = type.proxy_port
+ ----
+ module_eval src, __FILE__, lineno
end
- attr_reader :proxy_address, :proxy_port
+ def_http_method :Get, true, false
+ def_http_method :Head, false, false
+ def_http_method :Post, true, true
+ def_http_method :Put, false, true
- alias proxyaddr proxy_address
- alias proxyport proxy_port
-
- def proxy?
- true
- end
-
- def connect( addr = nil, port = nil )
- super @proxy_address, @proxy_port
- end
-
- def edit_path( path )
- 'http://' + address + (port == type.port ? '' : ":#{port}") + path
+ def request( req, *args )
+ common_oper( req ) {
+ req.__send__( :exec,
+ @socket, @curr_http_version, edit_path(req.path), *args )
+ yield req.response if block_given?
+ }
+ req.response
end
-
- end
-
- end # net private
+ private
- class HTTPResponseReceiver
-
- def initialize( command, body_exist )
- @command = command
- @body_exist = body_exist
- @header = @body = nil
- end
-
- def inspect
- "#<#{type}>"
- end
- def read_header
- unless @header then
- stream_check
- @header = @command.get_response
+ def common_oper( req )
+ req['connection'] ||= 'keep-alive'
+ if not @socket then
+ start
+ req['connection'] = 'close'
+ elsif @socket.closed? then
+ re_connect
end
- @header
- end
-
- alias header read_header
- alias response read_header
-
- def read_body( dest = nil, &block )
- unless @body then
- read_header
-
- to = procdest( dest, block )
- stream_check
-
- if @body_exist and @header.code_type.body_exist? then
- @command.get_body @header, to
- @header.body = @body = to
- else
- @command.no_body
- @header.body = nil
- @body = 1
- end
+ if not req.body_exist? or @seems_1_0_server then
+ req['connection'] = 'close'
end
- @body == 1 ? nil : @body
- end
-
- alias body read_body
- alias entity read_body
+ req['host'] = addr_port
- def terminate
- read_header
- read_body
- @command = nil
- end
+ yield req
+ req.response.__send__ :terminate
+ @curr_http_version = req.response.http_version
-
- private
-
- def stream_check
- unless @command then
- raise IOError, 'receiver was used out of block'
- end
- end
-
- def procdest( dest, block )
- if dest and block then
- raise ArgumentError,
- 'both of arg and block are given for HTTP method'
- end
- if block then
- NetPrivate::ReadAdapter.new block
+ if not req.response.body then
+ @socket.close
+ elsif keep_alive? req, req.response then
+ D 'Conn keep-alive'
+ if @socket.closed? then # (only) read stream had been closed
+ D 'Conn (but seems 1.0 server)'
+ @seems_1_0_server = true
+ @socket.close
+ end
else
- dest or ''
+ D 'Conn close'
+ @socket.close
end
- end
-
- end
-
- HTTPReadAdapter = HTTPResponseReceiver
-
- class HTTPResponse < Response
-
- def initialize( code_type, code, msg )
- super
- @data = {}
- @body = nil
+ req.response
end
- attr_accessor :body
+ def keep_alive?( req, res )
+ /close/i === req['connection'].to_s and return false
+ @seems_1_0_server and return false
- def inspect
- "#<#{type.name} #{code}>"
- end
+ /keep-alive/i === res['connection'].to_s and return true
+ /close/i === res['connection'].to_s and return false
+ /keep-alive/i === res['proxy-connection'].to_s and return true
+ /close/i === res['proxy-connection'].to_s and return false
- def []( key )
- @data[ key.downcase ]
+ @curr_http_version == '1.1' and return true
+ false
end
- def []=( key, val )
- @data[ key.downcase ] = val
- end
- def each( &block )
- @data.each( &block )
- end
+ #
+ # utils
+ #
- def each_key( &block )
- @data.each_key( &block )
- end
+ public
- def each_value( &block )
- @data.each_value( &block )
+ def self.get( addr, path, port = nil )
+ req = Get.new( path )
+ resp = nil
+ new( addr, port || HTTP.port ).start {|http|
+ resp = http.request( req )
+ }
+ resp.body
end
- def delete( key )
- @data.delete key.downcase
+ def self.get_print( addr, path, port = nil )
+ print get( addr, path, port )
end
- def key?( key )
- @data.key? key.downcase
- end
- def to_hash
- @data.dup
+ private
+
+ def addr_port
+ address + (port == HTTP.port ? '' : ":#{port}")
end
- def value
- unless SuccessCode === self then
- error! self
+ def D( msg )
+ if @dout then
+ @dout << msg
+ @dout << "\n"
end
end
end
+ HTTPSession = HTTP
+
+
class Code
@@ -776,101 +651,246 @@ module Net
HTTPVersionNotSupported = HTTPServerErrorCode.http_mkchild
- module NetPrivate
+ ###
+ ### header
+ ###
- class HTTPCommand < Command
+ net_private {
- HTTPVersion = '1.1'
+ module HTTPHeader
- def initialize( sock )
- @http_version = HTTPVersion
- super sock
+ def size
+ @header.size
end
- attr_reader :http_version
+ alias length size
- def inspect
- "#<Net::HTTPCommand>"
+ def []( key )
+ @header[ key.downcase ]
end
+ def []=( key, val )
+ @header[ key.downcase ] = val
+ end
- ###
- ### request
- ###
+ def each( &block )
+ @header.each( &block )
+ end
- public
+ def each_key( &block )
+ @header.each_key( &block )
+ end
- def get( path, u_header )
- return unless begin_critical
- request sprintf('GET %s HTTP/%s', path, HTTPVersion), u_header
+ def each_value( &block )
+ @header.each_value( &block )
end
-
- def head( path, u_header )
- return unless begin_critical
- request sprintf('HEAD %s HTTP/%s', path, HTTPVersion), u_header
+
+ def delete( key )
+ @header.delete key.downcase
+ end
+
+ def key?( key )
+ @header.key? key.downcase
+ end
+
+ def to_hash
+ @header.dup
end
- def post( path, u_header, data )
- return unless begin_critical
- u_header[ 'Content-Length' ] = data.size.to_s
- request sprintf('POST %s HTTP/%s', path, HTTPVersion), u_header
- @socket.write data
+ def canonical_each
+ @header.each do |k,v|
+ yield canonical(k), v
+ end
end
- def put( path, u_header, src )
- return unless begin_critical
- request sprintf('PUT %s HTTP/%s', path, HTTPVersion), u_header
- @socket.write_bin src
+ def canonical( k )
+ k.split('-').collect {|i| i.capitalize }.join('-')
end
- # def delete
+ def range
+ s = @header['range']
+ s or return nil
- # def trace
+ arr = []
+ s.split(',').each do |spec|
+ m = /bytes\s*=\s*(\d+)?\s*-\s*(\d+)?/i.match( spec )
+ m or raise HTTPHeaderSyntaxError, "wrong Range: #{spec}"
- # def options
+ 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)
+ else
+ raise HTTPHeaderSyntaxError, 'range is not specified'
+ end
+ end
- def quit
+ return *arr
end
+ def range=( r, fin = nil )
+ if fin then
+ r = r ... r+fin
+ end
- private
+ case r
+ when Numeric
+ s = r > 0 ? "0-#{r - 1}" : "-#{-r}"
+ when Range
+ first = r.first
+ last = r.last
+ if r.exclude_end? then
+ last -= 1
+ end
- def request( req, u_header )
- @socket.writeline req
- u_header.each do |n,v|
- @socket.writeline n + ': ' + v
+ if last == -1 then
+ s = first > 0 ? "#{first}-" : "-#{-first}"
+ else
+ first >= 0 or raise HTTPHeaderSyntaxError, 'range.first is negative'
+ last > 0 or raise HTTPHeaderSyntaxError, 'range.last is negative'
+ first < last or raise HTTPHeaderSyntaxError, 'must be .first < .last'
+ s = "#{first}-#{last}"
+ end
+ else
+ raise TypeError, 'Range/Integer is required'
end
- @socket.writeline ''
+
+ @header['range'] = "bytes=#{s}"
+ r
end
+ alias set_range range=
- ###
- ### response line & header
- ###
+ def content_length
+ s = @header['content-length']
+ s or return nil
- public
+ m = /\d+/.match(s)
+ m or raise HTTPHeaderSyntaxError, 'wrong Content-Length format'
+ m[0].to_i
+ end
- def get_response
- resp = get_resp0
- resp = get_resp0 while ContinueCode === resp
- resp
+ def chunked?
+ s = @header['transfer-encoding']
+ (s and /(?:\A|[^\-\w])chunked(?:[^\-\w]|\z)/i === s) ? true : false
+ end
+
+ def content_range
+ s = @header['content-range']
+ s or return nil
+
+ m = %r<bytes\s+(\d+)-(\d+)/(?:\d+|\*)>i.match( s )
+ m or raise HTTPHeaderSyntaxError, 'wrong Content-Range format'
+
+ m[1].to_i .. m[2].to_i + 1
+ end
+
+ def range_length
+ r = content_range
+ r and r.length
+ end
+
+ def basic_auth( acc, pass )
+ @header['authorization'] =
+ 'Basic ' + ["#{acc}:#{pass}"].pack('m').gsub(/\s+/, '')
+ end
+
+ end
+
+ }
+
+
+ ###
+ ### request
+ ###
+
+ net_private {
+
+ class HTTPRequest
+
+ include ::Net::NetPrivate::HTTPHeader
+
+ def initialize( path, uhead = nil )
+ @path = path
+ @header = tmp = {}
+ return unless uhead
+ uhead.each do |k,v|
+ key = k.downcase
+ if tmp.key? key then
+ $stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
+ end
+ tmp[ key ] = v.strip
+ end
+ tmp['accept'] ||= '*/*'
+
+ @socket = nil
+ @response = nil
+ end
+
+ attr_reader :path
+ attr_reader :response
+
+ def inspect
+ "\#<#{type}>"
+ end
+
+ def body_exist?
+ type::HAS_BODY
end
private
- def get_resp0
- resp = get_reply
+ #
+ # write
+ #
+
+ def exec( sock, ver, path )
+ ready( sock ) {
+ request ver, path
+ }
+ @response
+ end
+
+ def ready( sock )
+ @response = nil
+ @socket = sock
+ yield
+ @response = get_response
+ @socket = nil
+ end
+
+ def request( ver, path )
+ @socket.writeline sprintf('%s %s HTTP/%s', type::METHOD, path, ver)
+ canonical_each do |k,v|
+ @socket.writeline k + ': ' + v
+ end
+ @socket.writeline ''
+ end
+
+ #
+ # read
+ #
+
+ def get_response
+ begin
+ resp = read_response
+ end while ContinueCode === resp
+ resp
+ end
+
+ def read_response
+ resp = get_resline
while true do
- line = @socket.readline
+ line = @socket.readuntil( "\n", true ) # ignore EOF
+ line.sub!( /\s+\z/, '' ) # don't use chop!
break if line.empty?
m = /\A([^:]+):\s*/.match( line )
- unless m then
- raise HTTPBadResponse, 'wrong header line format'
- end
+ m or raise HTTPBadResponse, 'wrong header line format'
nm = m[1]
line = m.post_match
if resp.key? nm then
@@ -883,23 +903,117 @@ module Net
resp
end
- def get_reply
+ def get_resline
str = @socket.readline
- m = /\AHTTP\/(\d+\.\d+)?\s+(\d\d\d)\s*(.*)\z/i.match( str )
- unless m then
- raise HTTPBadResponse, "wrong status line: #{str}"
- end
- @http_version = m[1]
+ m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/i.match( str )
+ m or raise HTTPBadResponse, "wrong status line: #{str}"
+ httpver = m[1]
status = m[2]
discrip = m[3]
- code = HTTPCODE_TO_OBJ[status] ||
- HTTPCODE_CLASS_TO_OBJ[status[0,1]] ||
- UnknownCode
- HTTPResponse.new( code, status, discrip )
+ ::Net::NetPrivate::HTTPResponse.new(
+ status, discrip, @socket, type::HAS_BODY, httpver )
+ end
+
+ end
+
+
+ class HTTPRequestWithBody < HTTPRequest
+
+ private
+
+ def exec( sock, ver, path, str = nil )
+ check_arg str, block_given?
+
+ if block_given? then
+ ac = Accumulator.new
+ yield ac # must be yield, DO NOT USE block.call
+ data = ac.terminate
+ else
+ data = str
+ end
+ @header['content-length'] = data.size.to_s
+ @header.delete 'transfer-encoding'
+
+ ready( sock ) {
+ request ver, path
+ @socket.write data
+ }
+ @response
+ end
+
+ def check_arg( data, blkp )
+ if data and blkp then
+ raise ArgumentError, 'both of data and block given'
+ end
+ unless data or blkp then
+ raise ArgumentError, 'str or block required'
+ end
+ end
+
+ end
+
+
+ class Accumulator
+
+ def initialize
+ @buf = ''
+ end
+
+ def write( s )
+ @buf.concat s
+ end
+
+ alias << write
+
+ def terminate
+ ret = @buf
+ @buf = nil
+ ret
+ end
+
+ end
+
+ }
+
+
+ class HTTP
+
+ class Get < ::Net::NetPrivate::HTTPRequest
+ HAS_BODY = true
+ METHOD = 'GET'
+ end
+
+ class Head < ::Net::NetPrivate::HTTPRequest
+ HAS_BODY = false
+ METHOD = 'HEAD'
+ end
+
+ class Post < ::Net::NetPrivate::HTTPRequestWithBody
+ HAS_BODY = true
+ METHOD = 'POST'
+ end
+
+ class Put < ::Net::NetPrivate::HTTPRequestWithBody
+ HAS_BODY = true
+ METHOD = 'PUT'
end
- HTTPCODE_CLASS_TO_OBJ = {
+ end
+
+
+
+ ###
+ ### response
+ ###
+
+ net_private {
+
+ class HTTPResponse < Response
+
+ include ::Net::NetPrivate::HTTPHeader
+
+ CODE_CLASS_TO_OBJ = {
'1' => HTTPInformationCode,
'2' => HTTPSuccessCode,
'3' => HTTPRedirectionCode,
@@ -907,7 +1021,7 @@ module Net
'5' => HTTPServerErrorCode
}
- HTTPCODE_TO_OBJ = {
+ CODE_TO_OBJ = {
'100' => ContinueCode,
'101' => HTTPSwitchProtocol,
@@ -951,22 +1065,87 @@ module Net
'505' => HTTPVersionNotSupported
}
+ def initialize( stat, msg, sock, be, hv )
+ code = CODE_TO_OBJ[stat] ||
+ CODE_CLASS_TO_OBJ[stat[0,1]] ||
+ UnknownCode
+ super code, stat, msg
+ @socket = sock
+ @body_exist = be
+ @http_version = hv
- ###
- ### body
- ###
+ @header = {}
+ @body = nil
+ @read = false
+ end
- public
+ attr_reader :http_version
+
+ def inspect
+ "#<#{type} #{code}>"
+ end
+
+ def value
+ SuccessCode === self or error! self
+ end
- def get_body( resp, dest )
- if chunked? resp then
+
+ #
+ # header (for backward compatibility)
+ #
+
+ def read_header
+ self
+ end
+
+ alias header read_header
+ alias response read_header
+
+ #
+ # body
+ #
+
+ def read_body( dest = nil, &block )
+ if @read and (dest or block) then
+ raise IOError, "#{type}\#read_body called twice with argument"
+ end
+
+ unless @read then
+ to = procdest( dest, block )
+ stream_check
+
+ if @body_exist and code_type.body_exist? then
+ read_body_0 to
+ @body = to
+ else
+ @body = nil
+ end
+ @read = true
+ end
+
+ @body
+ end
+
+ alias body read_body
+ alias entity read_body
+
+
+ private
+
+
+ def terminate
+ read_body
+ end
+
+ def read_body_0( dest )
+ if chunked? then
read_chunked dest
else
- clen = content_length( resp )
+ clen = content_length
if clen then
- @socket.read clen, dest
+ @socket.read clen, dest, true # ignore EOF
else
- clen = range_length( resp )
+ clen = range_length
if clen then
@socket.read clen, dest
else
@@ -974,16 +1153,8 @@ module Net
end
end
end
- end_critical
- end
-
- def no_body
- end_critical
end
-
- private
-
def read_chunked( dest )
len = nil
total = 0
@@ -991,9 +1162,7 @@ module Net
while true do
line = @socket.readline
m = /[0-9a-fA-F]+/.match( line )
- unless m then
- raise HTTPBadResponse, "wrong chunk size line: #{line}"
- end
+ m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
len = m[0].hex
break if len == 0
@socket.read( len, dest ); total += len
@@ -1004,44 +1173,27 @@ module Net
end
end
- def content_length( resp )
- if resp.key? 'content-length' then
- m = /\d+/.match( resp['content-length'] )
- unless m then
- raise HTTPBadResponse, 'wrong Content-Length format'
- end
- m[0].to_i
- else
- nil
- end
+ def stream_check
+ @socket.closed? and raise IOError, 'try to read body out of block'
end
- def chunked?( resp )
- tmp = resp['transfer-encoding']
- tmp and /(?:\A|\s+)chunked(?:\s+|\z)/i === tmp
- end
-
- def range_length( resp )
- if resp.key? 'content-range' then
- m = %r<bytes\s+(\d+)-(\d+)/\d+>.match( resp['content-range'] )
- unless m then
- raise HTTPBadResponse, 'wrong Content-Range format'
- end
- l = m[2].to_i
- u = m[1].to_i
- if l > u then
- nil
- else
- u - l
- end
+ def procdest( dest, block )
+ if dest and block then
+ raise ArgumentError, 'both of arg and block are given for HTTP method'
+ end
+ if block then
+ ::Net::NetPrivate::ReadAdapter.new block
else
- nil
+ dest || ''
end
end
end
+ }
+
- end # module Net::NetPrivate
+ HTTPResponse = NetPrivate::HTTPResponse
+ HTTPResponseReceiver = NetPrivate::HTTPResponse
end # module Net
diff --git a/lib/net/imap.rb b/lib/net/imap.rb
index 34d324ed12..ea064d2552 100644
--- a/lib/net/imap.rb
+++ b/lib/net/imap.rb
@@ -7,9 +7,10 @@ Copyright (C) 2000 Shugo Maeda <shugo@ruby-lang.org>
This library is distributed under the terms of the Ruby license.
You can freely distribute/modify this library.
-== class Net::IMAP
+== Net::IMAP
Net::IMAP implements Internet Message Access Protocol (IMAP) clients.
+(The protocol is described in ((<[IMAP]>)).)
=== Super Class
@@ -102,21 +103,24 @@ Object
: list(refname, mailbox)
Sends a LIST command, and returns a subset of names from
the complete set of all names available to the client.
+ The return value is an array of ((<Net::IMAP::MailboxList>)).
ex).
imap.create("foo/bar")
imap.create("foo/baz")
p imap.list("", "foo/%")
- #=> [#<Net::IMAP::MailboxList attr=[:NoSelect], delim="/", name="foo/">, #<Net::IMAP::MailboxList attr=[:NoInferiors, :Marked], delim="/", name="foo/bar">, #<Net::IMAP::MailboxList attr=[:NoInferiors], delim="/", name="foo/baz">]
+ #=> [#<Net::IMAP::MailboxList attr=[:Noselect], delim="/", name="foo/">, #<Net::IMAP::MailboxList attr=[:Noinferiors, :Marked], delim="/", name="foo/bar">, #<Net::IMAP::MailboxList attr=[:Noinferiors], delim="/", name="foo/baz">]
: lsub(refname, mailbox)
Sends a LSUB command, and returns a subset of names from the set
of names that the user has declared as being "active" or
"subscribed".
+ The return value is an array of ((<Net::IMAP::MailboxList>)).
: status(mailbox, attr)
Sends a STATUS command, and returns the status of the indicated
mailbox.
+ The return value is a hash of attributes.
ex).
p imap.status("inbox", ["MESSAGES", "RECENT"])
@@ -166,6 +170,7 @@ Object
in the mailbox. the set parameter is a number or an array of
numbers or a Range object. the number is a message sequence
number (fetch) or a unique identifier (uid_fetch).
+ The return value is an array of ((<Net::IMAP::FetchData>)).
ex).
p imap.fetch(6..8, "UID")
@@ -188,6 +193,7 @@ Object
in the mailbox. the set parameter is a number or an array of
numbers or a Range object. the number is a message sequence
number (store) or a unique identifier (uid_store).
+ The return value is an array of ((<Net::IMAP::FetchData>)).
ex).
p imap.store(6..8, "+FLAGS", [:Deleted])
@@ -210,6 +216,443 @@ Object
p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
#=> [6, 7, 8, 1]
+== Net::IMAP::ContinuationRequest
+
+Net::IMAP::ContinuationRequest represents command continuation requests.
+
+The command continuation request response is indicated by a "+" token
+instead of a tag. This form of response indicates that the server is
+ready to accept the continuation of a command from the client. The
+remainder of this response is a line of text.
+
+ continue_req ::= "+" SPACE (resp_text / base64)
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: data
+ Returns the data (Net::IMAP::ResponseText).
+
+: raw_data
+ Returns the raw data string.
+
+== Net::IMAP::UntaggedResponse
+
+Net::IMAP::UntaggedResponse represents untagged responses.
+
+Data transmitted by the server to the client and status responses
+that do not indicate command completion are prefixed with the token
+"*", and are called untagged responses.
+
+ response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye /
+ mailbox_data / message_data / capability_data)
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: name
+ Returns the name such as "FLAGS", "LIST", "FETCH"....
+
+: data
+ Returns the data such as an array of flag symbols,
+ a ((<Net::IMAP::MailboxList>)) object....
+
+: raw_data
+ Returns the raw data string.
+
+== Net::IMAP::TaggedResponse
+
+Net::IMAP::TaggedResponse represents tagged responses.
+
+The server completion result response indicates the success or
+failure of the operation. It is tagged with the same tag as the
+client command which began the operation.
+
+ response_tagged ::= tag SPACE resp_cond_state CRLF
+
+ tag ::= 1*<any ATOM_CHAR except "+">
+
+ resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: tag
+ Returns the tag.
+
+: name
+ Returns the name. the name is one of "OK", "NO", "BAD".
+
+: data
+ Returns the data. See ((<Net::IMAP::ResponseText>)).
+
+: raw_data
+ Returns the raw data string.
+
+== Net::IMAP::ResponseText
+
+Net::IMAP::ResponseText represents texts of responses.
+The text may be prefixed by the response code.
+
+ resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text)
+ ;; text SHOULD NOT begin with "[" or "="
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: code
+ Returns the response code. See ((<Net::IMAP::ResponseCode>)).
+
+: text
+ Returns the text.
+
+== Net::IMAP::ResponseCode
+
+Net::IMAP::ResponseCode represents response codes.
+
+ resp_text_code ::= "ALERT" / "PARSE" /
+ "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" /
+ "READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
+ "UIDVALIDITY" SPACE nz_number /
+ "UNSEEN" SPACE nz_number /
+ atom [SPACE 1*<any TEXT_CHAR except "]">]
+
+=== SuperClass
+
+Struct
+
+=== Methods
+
+: name
+ Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY"....
+
+: data
+ Returns the data if it exists.
+
+== Net::IMAP::MailboxList
+
+Net::IMAP::MailboxList represents contents of the LIST response.
+
+ mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
+ "\Noselect" / "\Unmarked" / flag_extension) ")"
+ SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: attr
+ Returns the name attributes. Each name attribute is a symbol
+ capitalized by String#capitalize, such as :Noselect (not :NoSelect).
+
+: delim
+ Returns the hierarchy delimiter
+
+: name
+ Returns the mailbox name.
+
+== Net::IMAP::StatusData
+
+Net::IMAP::StatusData represents contents of the STATUS response.
+
+=== Super Class
+
+Object
+
+=== Methods
+
+: mailbox
+ Returns the mailbox name.
+
+: attr
+ Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT",
+ "UIDVALIDITY", "UNSEEN". Each value is a number.
+
+== Net::IMAP::FetchData
+
+Net::IMAP::FetchData represents contents of the FETCH response.
+
+=== Super Class
+
+Object
+
+=== Methods
+
+: seqno
+ Returns the message sequence number.
+ (Note: not the unique identifier, even for the UID command response.)
+
+: attr
+ Returns a hash. Each key is a data item name, and each value is
+ its value.
+
+ The current data items are:
+
+ : BODY
+ A form of BODYSTRUCTURE without extension data.
+ : BODY[<section>]<<origin_octet>>
+ A string expressing the body contents of the specified section.
+ : BODYSTRUCTURE
+ An object that describes the ((<[MIME-IMB]>)) body structure of a message.
+ See ((<Net::IMAP::BodyTypeBasic>)), ((<Net::IMAP::BodyTypeText>)),
+ ((<Net::IMAP::BodyTypeMessage>)), ((<Net::IMAP::BodyTypeMultipart>)).
+ : ENVELOPE
+ A ((<Net::IMAP::Envelope>)) object that describes the envelope
+ structure of a message.
+ : FLAGS
+ A array of flag symbols that are set for this message. flag symbols
+ are capitalized by String#capitalize.
+ : INTERNALDATE
+ A string representing the internal date of the message.
+ : RFC822
+ Equivalent to BODY[].
+ : RFC822.HEADER
+ Equivalent to BODY.PEEK[HEADER].
+ : RFC822.SIZE
+ A number expressing the ((<[RFC-822]>)) size of the message.
+ : RFC822.TEXT
+ Equivalent to BODY[TEXT].
+ : UID
+ A number expressing the unique identifier of the message.
+
+== Net::IMAP::Envelope
+
+Net::IMAP::Envelope represents envelope structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: date
+ Retunns a string that represents the date.
+
+: subject
+ Retunns a string that represents the subject.
+
+: from
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the from.
+
+: sender
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the sender.
+
+: reply_to
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the reply-to.
+
+: to
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the to.
+
+: cc
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the cc.
+
+: bcc
+ Retunns an array of ((<Net::IMAP::Address>)) that represents the bcc.
+
+: in_reply_to
+ Retunns a string that represents the in-reply-to.
+
+: message_id
+ Retunns a string that represents the message-id.
+
+== Net::IMAP::Address
+
+((<Net::IMAP::Address>)) represents electronic mail addresses.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: name
+ Returns the phrase from ((<[RFC-822]>)) mailbox.
+
+: route
+ Returns the route from ((<[RFC-822]>)) route-addr.
+
+: mailbox
+ nil indicates end of ((<[RFC-822]>)) group.
+ If non-nil and host is nil, returns ((<[RFC-822]>)) group name.
+ Otherwise, returns ((<[RFC-822]>)) local-part
+
+: host
+ nil indicates ((<[RFC-822]>)) group syntax.
+ Otherwise, returns ((<[RFC-822]>)) domain name.
+
+== Net::IMAP::ContentDisposition
+
+Net::IMAP::ContentDisposition represents Content-Disposition fields.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: dsp_type
+ Returns the disposition type.
+
+: param
+ Returns a hash that represents parameters of the Content-Disposition
+ field.
+
+== Net::IMAP::BodyTypeBasic
+
+Net::IMAP::BodyTypeBasic represents basic body structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: media_type
+ Returns the content media type name as defined in ((<[MIME-IMB]>)).
+
+: subtype
+ Returns the content subtype name as defined in ((<[MIME-IMB]>)).
+
+: param
+ Returns a hash that represents parameters as defined in
+ ((<[MIME-IMB]>)).
+
+: content_id
+ Returns a string giving the content id as defined in ((<[MIME-IMB]>)).
+
+: description
+ Returns a string giving the content description as defined in
+ ((<[MIME-IMB]>)).
+
+: encoding
+ Returns a string giving the content transfer encoding as defined in
+ ((<[MIME-IMB]>)).
+
+: size
+ Returns a number giving the size of the body in octets.
+
+: md5
+ Returns a string giving the body MD5 value as defined in ((<[MD5]>)).
+
+: disposition
+ Returns a ((<Net::IMAP::ContentDisposition>)) object giving
+ the content disposition.
+
+: language
+ Returns a string or an array of strings giving the body
+ language value as defined in [LANGUAGE-TAGS].
+
+: extension
+ Returns extension data.
+
+: multipart?
+ Returns false.
+
+== Net::IMAP::BodyTypeText
+
+Net::IMAP::BodyTypeText represents TEXT body structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: lines
+ Returns the size of the body in text lines.
+
+And Net::IMAP::BodyTypeText has all methods of ((<Net::IMAP::BodyTypeBasic>)).
+
+== Net::IMAP::BodyTypeMessage
+
+Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages.
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: envelope
+ Returns a ((<Net::IMAP::Envelope>)) giving the envelope structure.
+
+: body
+ Returns an object giving the body structure.
+
+And Net::IMAP::BodyTypeMessage has all methods of ((<Net::IMAP::BodyTypeText>)).
+
+== Net::IMAP::BodyTypeText
+
+=== Super Class
+
+Struct
+
+=== Methods
+
+: media_type
+ Returns the content media type name as defined in ((<[MIME-IMB]>)).
+
+: subtype
+ Returns the content subtype name as defined in ((<[MIME-IMB]>)).
+
+: parts
+ Returns multiple parts.
+
+: param
+ Returns a hash that represents parameters as defined in
+ ((<[MIME-IMB]>)).
+
+: disposition
+ Returns a ((<Net::IMAP::ContentDisposition>)) object giving
+ the content disposition.
+
+: language
+ Returns a string or an array of strings giving the body
+ language value as defined in [LANGUAGE-TAGS].
+
+: extension
+ Returns extension data.
+
+: multipart?
+ Returns true.
+
+== References
+
+: [IMAP]
+ M. Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1",
+ RFC 2060, December 1996.
+
+: [LANGUAGE-TAGS]
+ Alvestrand, H., "Tags for the Identification of
+ Languages", RFC 1766, March 1995.
+
+: [MD5]
+ Myers, J., and M. Rose, "The Content-MD5 Header Field", RFC
+ 1864, October 1995.
+
+: [MIME-IMB]
+ Freed, N., and N. Borenstein, "MIME (Multipurpose Internet
+ Mail Extensions) Part One: Format of Internet Message Bodies", RFC
+ 2045, November 1996.
+
+: [RFC-822]
+ Crocker, D., "Standard for the Format of ARPA Internet Text
+ Messages", STD 11, RFC 822, University of Delaware, August 1982.
+
=end
require "socket"
@@ -702,7 +1145,7 @@ module Net
Address = Struct.new(:name, :route, :mailbox, :host)
ContentDisposition = Struct.new(:dsp_type, :param)
- class BodyTypeBasic < Struct.new(:media_type, :media_subtype,
+ class BodyTypeBasic < Struct.new(:media_type, :subtype,
:param, :content_id,
:description, :encoding, :size,
:md5, :disposition, :language,
@@ -710,9 +1153,15 @@ module Net
def multipart?
return false
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
- class BodyTypeText < Struct.new(:media_type, :media_subtype,
+ class BodyTypeText < Struct.new(:media_type, :subtype,
:param, :content_id,
:description, :encoding, :size,
:lines,
@@ -721,9 +1170,15 @@ module Net
def multipart?
return false
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
- class BodyTypeMessage < Struct.new(:media_type, :media_subtype,
+ class BodyTypeMessage < Struct.new(:media_type, :subtype,
:param, :content_id,
:description, :encoding, :size,
:envelope, :body, :lines,
@@ -732,15 +1187,27 @@ module Net
def multipart?
return false
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
- class BodyTypeMultipart < Struct.new(:media_type, :media_subtype,
+ class BodyTypeMultipart < Struct.new(:media_type, :subtype,
:parts,
:param, :disposition, :language,
:extension)
def multipart?
return true
end
+
+ def media_subtype
+ $stderr.printf("warning: media_subtype is obsolete.\n")
+ $stderr.printf(" use subtype instead.\n")
+ return subtype
+ end
end
class ResponseParser
@@ -1475,6 +1942,12 @@ module Net
when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n
match(T_SPACE)
result = ResponseCode.new(name, number)
+ else
+ match(T_SPACE)
+ @lex_state = EXPR_CTEXT
+ token = match(T_TEXT)
+ @lex_state = EXPR_BEG
+ result = ResponseCode.new(name, token.value)
end
match(T_RBRA)
@lex_state = EXPR_RTEXT
@@ -1579,7 +2052,7 @@ module Net
if @str.index(/\(([^)]*)\)/ni, @pos)
@pos = $~.end(0)
return $1.scan(FLAG_REGEXP).collect { |flag, atom|
- atom || flag.intern
+ atom || flag.capitalize.intern
}
else
parse_error("invalid flag list")
diff --git a/lib/net/pop.rb b/lib/net/pop.rb
index 4f6eb930a4..8f3f978e8c 100644
--- a/lib/net/pop.rb
+++ b/lib/net/pop.rb
@@ -1,6 +1,6 @@
=begin
-= net/pop.rb version 1.1.32
+= net/pop.rb version 1.1.34
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -184,6 +184,7 @@ module Net
protocol_param :port, '110'
protocol_param :command_type, '::Net::NetPrivate::POP3Command'
+ protocol_param :apop_command_type, '::Net::NetPrivate::APOPCommand'
protocol_param :mail_type, '::Net::POPMail'
@@ -206,9 +207,10 @@ module Net
end
- def initialize( addr = nil, port = nil )
- super
+ def initialize( addr = nil, port = nil, apop = false )
+ super addr, port
@mails = nil
+ @apop = false
end
attr :mails
@@ -238,6 +240,11 @@ module Net
private
+ def conn_command( sock )
+ @command =
+ (@apop ? type.apop_command_type : type.command_type).new(sock)
+ end
+
def do_start( acnt, pwd )
@command.auth( acnt, pwd )
diff --git a/lib/net/protocol.rb b/lib/net/protocol.rb
index 161024cfe2..343721add3 100644
--- a/lib/net/protocol.rb
+++ b/lib/net/protocol.rb
@@ -1,6 +1,6 @@
=begin
-= net/protocol.rb version 1.1.32
+= net/protocol.rb version 1.1.34
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -59,13 +59,22 @@ Object
=end
require 'socket'
+require 'timeout'
module Net
+ module NetPrivate
+ end
+
+ def self.net_private( &block )
+ ::Net::NetPrivate.module_eval( &block )
+ end
+
+
class Protocol
- Version = '1.1.32'
+ Version = '1.1.34'
class << self
@@ -116,8 +125,12 @@ module Net
@command = nil
@socket = nil
- @active = false
- @pipe = nil
+ @active = false
+
+ @open_timeout = nil
+ @read_timeout = nil
+
+ @dout = nil
end
attr_reader :address
@@ -126,10 +139,26 @@ module Net
attr_reader :command
attr_reader :socket
+ attr_accessor :open_timeout
+ attr_accessor :read_timeout
+
+ def active?
+ @active
+ end
+
+ def set_debug_output( arg ) # un-documented
+ @dout = arg
+ end
+
+ alias set_pipe set_debug_output
+
def inspect
"#<#{type} #{address}:#{port} open=#{active?}>"
end
+ #
+ # open session
+ #
def start( *args )
return false if active?
@@ -146,45 +175,59 @@ module Net
end
end
+ private
+
def _start( args )
connect
do_start( *args )
@active = true
end
- private :_start
-
- def finish
- return false unless active?
- do_finish unless @command.critical?
- disconnect
- @active = false
- true
+ def connect
+ conn_socket @address, @port
+ conn_command @socket
+ on_connect
end
- def active?
- @active
+ def re_connect
+ @socket.reopen @open_timeout
+ on_connect
end
- def set_pipe( arg ) # un-documented
- @pipe = arg
+ def conn_socket( addr, port )
+ @socket = type.socket_type.open(
+ addr, port, @open_timeout, @read_timeout, @dout )
end
+ def conn_command( sock )
+ @command = type.command_type.new( sock )
+ end
- private
-
+ def on_connect
+ end
def do_start
end
- def do_finish
- @command.quit
+ #
+ # close session
+ #
+
+ public
+
+ def finish
+ return false unless active?
+
+ do_finish if @command and not @command.critical?
+ disconnect
+ @active = false
+ true
end
+ private
- def connect( addr = @address, port = @port )
- @socket = type.socket_type.open( addr, port, @pipe )
- @command = type.command_type.new( @socket )
+ def do_finish
+ @command.quit
end
def disconnect
@@ -192,7 +235,11 @@ module Net
if @socket and not @socket.closed? then
@socket.close
end
- @socket = nil
+ @socket = nil
+ on_disconnect
+ end
+
+ def on_disconnect
end
end
@@ -200,6 +247,7 @@ module Net
Session = Protocol
+ net_private {
class Response
@@ -223,6 +271,8 @@ module Net
end
+ }
+
class ProtocolError < StandardError; end
class ProtoSyntaxError < ProtocolError; end
@@ -294,8 +344,7 @@ module Net
- module NetPrivate
-
+ net_private {
class WriteAdapter
@@ -311,7 +360,11 @@ module Net
def write( str )
@sock.__send__ @mid, str
end
- alias << write
+
+ def <<( str )
+ @sock.__send__ @mid, str
+ self
+ end
end
@@ -407,6 +460,7 @@ module Net
@critical = false
end
+
private
def critical
@@ -431,22 +485,30 @@ module Net
class Socket
- def initialize( addr, port, pipe = nil )
+ def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
@addr = addr
@port = port
- @pipe = pipe
- @prepipe = nil
- @closed = true
- @ipaddr = ''
+ @read_timeout = rtime
+
+ @debugout = dout
+
+ @socket = nil
@sending = ''
@buffer = ''
- @socket = TCPsocket.new( addr, port )
- @closed = false
- @ipaddr = @socket.addr[3]
+ connect otime
+ D 'opened'
end
+ def connect( otime )
+ D "opening connection to #{@addr}..."
+ timeout( otime ) {
+ @socket = TCPsocket.new( @addr, @port )
+ }
+ end
+ private :connect
+
attr :pipe, true
class << self
@@ -454,27 +516,31 @@ module Net
end
def inspect
- "#<#{type} open=#{!@closed}>"
+ "#<#{type} #{closed? ? 'closed' : 'opened'}>"
end
- def reopen
- unless closed? then
- close
- @buffer = ''
- end
- @socket = TCPsocket.new( @addr, @port )
- @closed = false
+ def reopen( otime = nil )
+ D 'reopening...'
+ close
+ connect otime
+ D 'reopened'
end
attr :socket, true
def close
- @socket.close
- @closed = true
+ if @socket then
+ @socket.close
+ D 'closed'
+ else
+ D 'close call for already closed socket'
+ end
+ @socket = nil
+ @buffer = ''
end
def closed?
- @closed
+ not @socket
end
def address
@@ -486,7 +552,8 @@ module Net
attr_reader :port
def ip_address
- @ipaddr.dup
+ @socket or return ''
+ @socket.addr[3]
end
alias ipaddr ip_address
@@ -494,57 +561,64 @@ module Net
attr_reader :sending
- ###
- ### read
- ###
+ #
+ # read
+ #
+
+ public
CRLF = "\r\n"
- def read( len, dest = '' )
- @pipe << "reading #{len} bytes...\n" if @pipe; pipeoff
+ def read( len, dest = '', ignerr = false )
+ D_off "reading #{len} bytes..."
rsize = 0
- while rsize + @buffer.size < len do
- rsize += writeinto( dest, @buffer.size )
- fill_rbuf
+ begin
+ while rsize + @buffer.size < len do
+ rsize += rbuf_moveto( dest, @buffer.size )
+ rbuf_fill
+ end
+ rbuf_moveto dest, len - rsize
+ rescue EOFError
+ raise unless igneof
end
- writeinto( dest, len - rsize )
- @pipe << "read #{len} bytes\n" if pipeon
+ D_on "read #{len} bytes"
dest
end
-
def read_all( dest = '' )
- @pipe << "reading all...\n" if @pipe; pipeoff
+ D_off 'reading all...'
rsize = 0
begin
while true do
- rsize += writeinto( dest, @buffer.size )
- fill_rbuf
+ rsize += rbuf_moveto( dest, @buffer.size )
+ rbuf_fill
end
rescue EOFError
;
end
- @pipe << "read #{rsize} bytes\n" if pipeon
+ D_on "read #{rsize} bytes"
dest
end
-
- def readuntil( target )
- while true do
- idx = @buffer.index( target )
- break if idx
- fill_rbuf
- end
-
+ def readuntil( target, igneof = false )
dest = ''
- writeinto( dest, idx + target.size )
+ begin
+ while true do
+ idx = @buffer.index( target )
+ break if idx
+ rbuf_fill
+ end
+ rbuf_moveto dest, idx + target.size
+ rescue EOFError
+ raise unless igneof
+ rbuf_moveto dest, @buffer.size
+ end
dest
end
-
def readline
ret = readuntil( "\n" )
@@ -552,9 +626,8 @@ module Net
ret
end
-
def read_pendstr( dest )
- @pipe << "reading text...\n" if @pipe; pipeoff
+ D_off 'reading text...'
rsize = 0
while (str = readuntil("\r\n")) != ".\r\n" do
@@ -563,14 +636,13 @@ module Net
dest << str
end
- @pipe << "read #{rsize} bytes\n" if pipeon
+ D_on "read #{rsize} bytes"
dest
end
-
# private use only (can not handle 'break')
def read_pendlist
- @pipe << "reading list...\n" if @pipe; pipeoff
+ D_off 'reading list...'
str = nil
i = 0
@@ -580,55 +652,59 @@ module Net
yield str
end
- @pipe << "read #{i} items\n" if pipeon
+ D_on "read #{i} items"
end
private
- READ_BLOCK = 1024 * 8
+ READ_SIZE = 1024 * 4
- def fill_rbuf
- @buffer << @socket.sysread( READ_BLOCK )
+ def rbuf_fill
+ unless IO.select [@socket], nil, nil, @read_timeout then
+ on_read_timeout
+ end
+ @buffer << @socket.sysread( READ_SIZE )
end
- def writeinto( dest, len )
+ def on_read_timeout
+ raise TimeoutError, "socket read timeout (#{@read_timeout} sec)"
+ end
+
+ def rbuf_moveto( dest, len )
bsi = @buffer.size
- dest << @buffer[ 0, len ]
+ s = @buffer[ 0, len ]
+ dest << s
@buffer = @buffer[ len, bsi - len ]
- @pipe << %{read "#{Net.quote dest}"\n} if @pipe
+ @debugout << %<read "#{Net.quote s}"\n> if @debugout
len
end
- ###
- ### write
- ###
+ #
+ # write interfece
+ #
public
-
def write( str )
writing {
do_write str
}
end
-
def writeline( str )
writing {
- do_write str
- do_write "\r\n"
+ do_write str + "\r\n"
}
end
-
def write_bin( src, block )
writing {
if block then
- block.call WriteAdapter.new( self, :do_write )
+ block.call ::Net::NetPrivate::WriteAdapter.new( self, :do_write )
else
src.each do |bin|
do_write bin
@@ -637,19 +713,18 @@ module Net
}
end
-
def write_pendstr( src, block )
- @pipe << "writing text from #{src.type}\n" if @pipe; pipeoff
+ D_off "writing text from #{src.type}"
wsize = use_each_crlf_line {
if block then
- block.call WriteAdapter.new( self, :wpend_in )
+ block.call ::Net::NetPrivate::WriteAdapter.new( self, :wpend_in )
else
wpend_in src
end
}
- @pipe << "wrote #{wsize} bytes text\n" if pipeon
+ D_on "wrote #{wsize} bytes text"
wsize
end
@@ -696,17 +771,17 @@ module Net
beg = 0
buf = @wbuf
while buf.index( /\n|\r\n|\r/, beg ) do
- m = $~
+ m = Regexp.last_match
if m.begin(0) == buf.size - 1 and buf[-1] == ?\r then
# "...\r" : can follow "\n..."
break
end
- str = buf[ beg, m.begin(0) - beg ]
+ str = buf[ beg ... m.begin(0) ]
str.concat "\r\n"
yield str
beg = m.end(0)
end
- @wbuf = buf[ beg, buf.size - beg ]
+ @wbuf = buf[ beg ... buf.size ]
end
end
@@ -736,6 +811,7 @@ module Net
yield
end
end
+ yield unless @wbuf.empty?
end
end
@@ -746,17 +822,17 @@ module Net
yield
- if @pipe then
- @pipe << 'write "'
- @pipe << @sending
- @pipe << "\"\n"
+ if @debugout then
+ @debugout << 'write "'
+ @debugout << @sending
+ @debugout << "\"\n"
end
@socket.flush
@writtensize
end
def do_write( arg )
- if @pipe or @sending.size < 128 then
+ if @debugout or @sending.size < 128 then
@sending << Net.quote( arg )
else
@sending << '...' unless @sending[-1] == ?.
@@ -768,22 +844,25 @@ module Net
end
- def pipeoff
- @prepipe = @pipe
- @pipe = nil
- @prepipe
+ def D_off( msg )
+ D msg
+ @savedo, @debugout = @debugout, nil
end
- def pipeon
- @pipe = @prepipe
- @prepipe = nil
- @pipe
+ def D_on( msg )
+ @debugout = @savedo
+ D msg
end
- end
+ def D( msg )
+ @debugout or return
+ @debugout << msg
+ @debugout << "\n"
+ end
+ end
- end # module Net::NetPrivate
+ }
def Net.quote( str )
diff --git a/lib/net/smtp.rb b/lib/net/smtp.rb
index 9679984e2c..befc1adf03 100644
--- a/lib/net/smtp.rb
+++ b/lib/net/smtp.rb
@@ -1,6 +1,6 @@
=begin
-= net/smtp.rb version 1.1.32
+= net/smtp.rb version 1.1.34
written by Minero Aoki <aamine@dp.u-netsurf.ne.jp>
@@ -30,10 +30,8 @@ Net::Protocol
=== Methods
-: start( helo_domain = Socket.gethostname, \
- account = nil, password = nil, authtype = nil )
-: start( helo_domain = Socket.gethostname, \
- account = nil, password = nil, authtype = nil ) {|smtp| .... }
+: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil )
+: start( helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... }
opens TCP connection and starts SMTP session.
If protocol had been started, do nothing and return false.
@@ -53,10 +51,10 @@ Net::Protocol
to_addrs must be a String(s) or an Array of String.
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)
+ * Net::ProtoSyntaxError: syntax error (errno.500)
+ * Net::ProtoFatalError: fatal error (errno.550)
+ * Net::ProtoUnknownError: unknown error
+ * Net::ProtoServerBusy: temporary error (errno.420/450)
# usage example
@@ -153,12 +151,15 @@ module Net
end
end
- if user and secret then
+ if user or secret then
+ (user and secret) or
+ raise ArgumentError, 'both of account and password are required'
+
mid = 'auth_' + (authtype || 'cram_md5').to_s
- unless @command.respond_to? mid then
- raise ArgumentError, "wrong auth type #{authtype.to_s}"
- end
- @command.send mid, user, secret
+ @command.respond_to? mid or
+ raise ArgumentError, "wrong auth type #{authtype.to_s}"
+
+ @command.__send__ mid, user, secret
end
end
diff --git a/lib/net/telnet.rb b/lib/net/telnet.rb
index 87790c0300..380e834bea 100644
--- a/lib/net/telnet.rb
+++ b/lib/net/telnet.rb
@@ -4,7 +4,7 @@
net/telnet.rb - simple telnet client library
-Version 1.6.2
+Version 1.6.3
Wakou Aoyama <wakou@fsinet.or.jp>
@@ -239,10 +239,11 @@ module Net
CR = "\015"
LF = "\012"
EOL = CR + LF
- VERSION = "1.6.2"
- RELEASE_DATE = "2000-12-25"
- VERSION_CODE = 162
- RELEASE_CODE = 20001225
+ VERSION = '1.6.3'
+ RELEASE_DATE = '2001-02-26'
+ VERSION_CODE = 163
+ RELEASE_CODE = 20010226
+ REVISION = '$Id$'
def initialize(options)
@options = options
@@ -346,14 +347,13 @@ module Net
attr :sock
def telnetmode(mode = nil)
- if mode
- if (true == mode or false == mode)
- @options["Telnetmode"] = mode
- else
- raise ArgumentError, "required true or false"
- end
- else
+ case mode
+ when nil
@options["Telnetmode"]
+ when true, false
+ @options["Telnetmode"] = mode
+ else
+ raise ArgumentError, "required true or false"
end
end
@@ -366,14 +366,13 @@ module Net
end
def binmode(mode = nil)
- if mode
- if (true == mode or false == mode)
- @options["Binmode"] = mode
- else
- raise ArgumentError, "required true or false"
- end
- else
+ case mode
+ when nil
@options["Binmode"]
+ when true, false
+ @options["Binmode"] = mode
+ else
+ raise ArgumentError, "required true or false"
end
end
@@ -599,181 +598,7 @@ end
== HISTORY
-* Mon Dec 25 01:37:43 JST 2000 - wakou
- * version 1.6.2
- * Regexp::last_match[1] --> $1
-
-* Mon Dec 11 00:16:51 JST 2000 - wakou
- * version 1.6.1
- * $1 --> Regexp::last_match[1]
-
-* 2000/09/12 05:37:35 - matz
- * change: iterator? --> block_given?
-
-* Tue Sep 12 06:52:48 JST 2000 - wakou
- * version 1.6.0
- * correct: document.
- thanks to Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
- * add: Telnet#puts().
-
-* Sun Jun 18 23:31:44 JST 2000 - wakou
- * version 1.5.0
- * change: version syntax. old: x.yz, now: x.y.z
-
-* 2000/05/24 06:57:38 - wakou
- * version 1.40
- * improve: binmode(), telnetmode() interface.
- thanks to Dave Thomas <Dave@thomases.com>
-
-* 2000/05/09 22:02:56 - wakou
- * version 1.32
- * require English.rb
-
-* 2000/05/02 21:48:39 - wakou
- * version 1.31
- * Proxy option: can receive IO object.
-
-* 2000/04/03 18:27:02 - wakou
- * version 1.30
- * telnet.rb --> net/telnet.rb
-
-* 2000/01/24 17:02:57 - wakou
- * version 1.20
- * respond to "IAC WILL x" with "IAC DONT x"
- * respond to "IAC WONT x" with "IAC DONT x"
- * better dumplog format.
- thanks to WATANABE Hirofumi <Hirofumi.Watanabe@jp.sony.com>
-
-* 2000/01/18 17:47:31 - wakou
- * version 1.10
- * bug fix: write method
- * respond to "IAC WILL BINARY" with "IAC DO BINARY"
-
-* 1999/10/04 22:51:26 - wakou
- * version 1.00
- * bug fix: waitfor(preprocess) method.
- thanks to Shin-ichiro Hara <sinara@blade.nagaokaut.ac.jp>
- * add simple support for AO, DM, IP, NOP, SB, SE
- * COUTION! TimeOut --> TimeoutError
-
-* 1999/09/21 21:24:07 - wakou
- * version 0.50
- * add write method
-
-* 1999/09/17 17:41:41 - wakou
- * version 0.40
- * bug fix: preprocess method
-
-* 1999/09/14 23:09:05 - wakou
- * version 0.30
- * change prompt check order.
- not IO::select([@sock], nil, nil, waittime) and prompt === line
- --> prompt === line and not IO::select([@sock], nil, nil, waittime)
-
-* 1999/09/13 22:28:33 - wakou
- * version 0.24
- * Telnet#login: if ommit password, then not require password prompt.
-
-* 1999/08/10 05:20:21 - wakou
- * version 0.232
- * STATUS OUTPUT sample code typo.
- thanks to Tadayoshi Funaba <tadf@kt.rim.or.jp>
- host = Telnet.new({"Hosh" => "localhost"){|c| print c }
- --> host = Telnet.new({"Host" => "localhost"){|c| print c }
-
-* 1999/07/16 13:39:42 - wakou
- * version 0.231
- * TRUE --> true, FALSE --> false
-
-* 1999/07/15 22:32:09 - wakou
- * version 0.23
- * waitfor: if end of file reached, then return nil.
-
-* 1999/06/29 09:08:51 - wakou
- * version 0.22
- * new, waitfor, cmd: {"Timeout" => false} # ignore timeout
-
-* 1999/06/28 18:18:55 - wakou
- * version 0.21
- * waitfor: not rescue (EOFError)
-
-* 1999/06/04 06:24:58 - wakou
- * version 0.20
- * waitfor: support for divided telnet command
-
-* 1999/05/22 - wakou
- * version 0.181
- * bug fix: print method
-
-* 1999/05/14 - wakou
- * version 0.18
- * respond to "IAC WON'T SGA" with "IAC DON'T SGA"
- * DON'T SGA : end of line --> CR + LF
- * bug fix: preprocess method
-
-* 1999/04/30 - wakou
- * version 0.17
- * bug fix: $! + "\n" --> $!.to_s + "\n"
-
-* 1999/04/11 - wakou
- * version 0.163
- * STDOUT.write(message) --> yield(message) if iterator?
-
-* 1999/03/17 - wakou
- * version 0.162
- * add "Proxy" option
- * required timeout.rb
-
-* 1999/02/03 - wakou
- * version 0.161
- * select --> IO::select
-
-* 1998/10/09 - wakou
- * version 0.16
- * preprocess method change for the better
- * add binmode method.
- * change default Binmode. TRUE --> FALSE
-
-* 1998/10/04 - wakou
- * version 0.15
- * add telnetmode method.
-
-* 1998/09/22 - wakou
- * version 0.141
- * change default prompt. /[$%#>] $/ --> /[$%#>] \Z/
-
-* 1998/09/01 - wakou
- * version 0.14
- * IAC WILL SGA send EOL --> CR+NULL
- * IAC WILL SGA IAC DO BIN send EOL --> CR
- * NONE send EOL --> LF
- * add Dump_log option.
-
-* 1998/08/25 - wakou
- * version 0.13
- * add print method.
-
-* 1998/08/05 - wakou
- * version 0.122
- * support for HP-UX 10.20.
- thanks to WATANABE Tetsuya <tetsu@jpn.hp.com>
- * socket.<< --> socket.write
-
-* 1998/07/15 - wakou
- * version 0.121
- * string.+= --> string.concat
-
-* 1998/06/01 - wakou
- * version 0.12
- * add timeout, waittime.
-
-* 1998/04/21 - wakou
- * version 0.11
- * add realtime output.
-
-* 1998/04/13 - wakou
- * version 0.10
- * first release.
-
-$Date$
+delete. see cvs log.
+
+
=end