aamine
* lib/net/protocol.rb: Protocol#start returns the return value of block. * lib/net/protocol.rb: set timeout limit by default. * lib/net/protocol.rb: new methods WriteAdapter#write, puts, print, printf. * lib/net/http.rb: rename HTTP#get2 to request_get, post2 to request_post ... * lib/net/smtp.rb: should not resolve HELO domain automatically. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1951 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
653f326bb1
commit
f3d9a0cc21
15
ChangeLog
15
ChangeLog
@ -1,3 +1,18 @@
|
|||||||
|
Mon Dec 31 04:27:28 2001 Minero Aoki <aamine@mx.edit.ne.jp>
|
||||||
|
|
||||||
|
* lib/net/protocol.rb: Protocol#start returns the return value of
|
||||||
|
block.
|
||||||
|
|
||||||
|
* lib/net/protocol.rb: set timeout limit by default.
|
||||||
|
|
||||||
|
* lib/net/protocol.rb: new methods WriteAdapter#write, puts,
|
||||||
|
print, printf.
|
||||||
|
|
||||||
|
* lib/net/http.rb: rename HTTP#get2 to request_get, post2 to
|
||||||
|
request_post ...
|
||||||
|
|
||||||
|
* lib/net/smtp.rb: should not resolve HELO domain automatically.
|
||||||
|
|
||||||
Sun Dec 30 00:59:16 2001 WATANABE Hirofumi <eban@ruby-lang.org>
|
Sun Dec 30 00:59:16 2001 WATANABE Hirofumi <eban@ruby-lang.org>
|
||||||
|
|
||||||
* ext/extmk.rb.in, lib/mkmf.rb (have_library): accept -lm
|
* ext/extmk.rb.in, lib/mkmf.rb (have_library): accept -lm
|
||||||
|
@ -204,15 +204,16 @@ Ruby 1.6
|
|||||||
プロクシ経由で接続する HTTP オブジェクトならプロクシのポート。
|
プロクシ経由で接続する HTTP オブジェクトならプロクシのポート。
|
||||||
そうでないなら nil。
|
そうでないなら nil。
|
||||||
|
|
||||||
: get( path, header = nil, dest = '' )
|
: get( path, header = nil )
|
||||||
: get( path, header = nil ) {|str| .... }
|
: get( path, header = nil ) {|str| .... }
|
||||||
サーバ上の path にあるエンティティを取得し、dest に << メソッドを
|
サーバ上の path にあるエンティティを取得します。また header が nil
|
||||||
使って書きこみます。また header が nil でなければリクエストを送る
|
でなければ、リクエストを送るときにその内容を HTTP ヘッダとして書き
|
||||||
ときにその内容を HTTP ヘッダとして書きこみます。header はハッシュで、
|
こみます。header はハッシュで、「ヘッダ名 => 内容」のような形式で
|
||||||
「ヘッダ名 => 内容」のような形式でなければいけません。
|
なければいけません。
|
||||||
|
|
||||||
返り値は、バージョン 1.1 では HTTPResponse と dest 二要素の配列です。
|
返り値は、バージョン 1.1 では HTTPResponse とエンティティボディ文字列の
|
||||||
1.2 では HTTPResponse ただひとつのみです。
|
二要素の配列です。1.2 では HTTPResponse ただひとつのみです。この場合、
|
||||||
|
エンティティボディは response.body で得られます。
|
||||||
|
|
||||||
ブロックとともに呼ばれた時はエンティティボディを少しづつブロックに
|
ブロックとともに呼ばれた時はエンティティボディを少しづつブロックに
|
||||||
与えます。
|
与えます。
|
||||||
@ -237,10 +238,6 @@ Ruby 1.6
|
|||||||
f.write str
|
f.write str
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
# same effect
|
|
||||||
File.open( 'save.txt', 'w' ) {|f|
|
|
||||||
http.get '/~foo/', nil, f
|
|
||||||
}
|
|
||||||
|
|
||||||
: head( path, header = nil )
|
: head( path, header = nil )
|
||||||
サーバ上の path にあるエンティティのヘッダのみを取得します。
|
サーバ上の path にあるエンティティのヘッダのみを取得します。
|
||||||
@ -260,7 +257,7 @@ Ruby 1.6
|
|||||||
}
|
}
|
||||||
p response['content-type']
|
p response['content-type']
|
||||||
|
|
||||||
: post( path, data, header = nil, dest = '' )
|
: post( path, data, header = nil )
|
||||||
: post( path, data, header = nil ) {|str| .... }
|
: post( path, data, header = nil ) {|str| .... }
|
||||||
サーバ上の path にあるエンティティに対し文字列 data を
|
サーバ上の path にあるエンティティに対し文字列 data を
|
||||||
送ります。レスポンスは << メソッドを使って dest に書き
|
送ります。レスポンスは << メソッドを使って dest に書き
|
||||||
@ -275,55 +272,54 @@ Ruby 1.6
|
|||||||
一方 1.2 では全く例外を発生しません。
|
一方 1.2 では全く例外を発生しません。
|
||||||
|
|
||||||
# version 1.1
|
# version 1.1
|
||||||
response, body = http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' )
|
response, body = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
||||||
|
|
||||||
# version 1.2
|
# version 1.2
|
||||||
response = http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' )
|
response = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
||||||
# compatible for both version
|
|
||||||
response , = http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' )
|
# compatible in both version
|
||||||
|
response , = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
||||||
|
|
||||||
# using block
|
# using block
|
||||||
File.open( 'save.html', 'w' ) {|f|
|
File.open( 'save.html', 'w' ) {|f|
|
||||||
http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' ) do |str|
|
http.post( '/cgi-bin/search.rb',
|
||||||
|
'query=subject&target=ruby' ) do |str|
|
||||||
f.write str
|
f.write str
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
# same effect
|
|
||||||
File.open( 'save.html', 'w' ) {|f|
|
|
||||||
http.post '/cgi-bin/search.rb', 'querytype=subject&target=ruby', nil, f
|
|
||||||
}
|
|
||||||
|
|
||||||
: get2( path, header = nil )
|
: request_get( path, header = nil )
|
||||||
: get2( path, header = nil ) {|response| .... }
|
: request_get( path, header = nil ) {|response| .... }
|
||||||
path にあるエンティティを取得します。HTTPResponse
|
path にあるエンティティを取得します。HTTPResponse
|
||||||
オブジェクトを返します。
|
オブジェクトを返します。
|
||||||
|
|
||||||
ブロックとともに呼び出されたときは、ブロック実行中は接続を
|
ブロックとともに呼び出されたときは、ブロック実行中は接続を
|
||||||
維持したまま HTTPResponse オブジェクトをブロックに渡します。
|
維持したまま HTTPResponse オブジェクトをブロックに渡します。
|
||||||
|
|
||||||
このメソッドはステータスに関らず例外を発生させません。
|
このメソッドは HTTP プロトコルに関連した例外は発生させません。
|
||||||
|
|
||||||
# example
|
# example
|
||||||
response = http.get2( '/index.html' )
|
response = http.request_get( '/index.html' )
|
||||||
p response['content-type']
|
p response['content-type']
|
||||||
puts response.body # body is already read
|
puts response.body # body is already read
|
||||||
|
|
||||||
# using block
|
# using block
|
||||||
http.get2( '/index.html' ) {|response|
|
http.request_get( '/index.html' ) {|response|
|
||||||
p response['content-type']
|
p response['content-type']
|
||||||
response.read_body do |str| # read body now
|
response.read_body do |str| # read body now
|
||||||
print str
|
print str
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
: post2( path, header = nil )
|
: request_post( path, data, header = nil )
|
||||||
: post2( path, header = nil ) {|response| .... }
|
: request_post( path, data, header = nil ) {|response| .... }
|
||||||
path にあるエンティティを取得します。HTTPResponse
|
path にあるエンティティを取得します。HTTPResponse
|
||||||
オブジェクトを返します。
|
オブジェクトを返します。
|
||||||
|
|
||||||
ブロックとともに呼び出されたときは、ボディを読みこむ前に
|
ブロックとともに呼び出されたときは、ボディを読みこむ前に
|
||||||
HTTPResponse オブジェクトをブロックに渡します。
|
HTTPResponse オブジェクトをブロックに渡します。
|
||||||
|
|
||||||
このメソッドはステータスに関らず例外を発生させません。
|
このメソッドは HTTP プロトコルに関連した例外は発生させません。
|
||||||
|
|
||||||
# example
|
# example
|
||||||
response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
|
response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
|
||||||
@ -341,12 +337,14 @@ Ruby 1.6
|
|||||||
|
|
||||||
: request( request [, data] )
|
: request( request [, data] )
|
||||||
: request( request [, data] ) {|response| .... }
|
: request( request [, data] ) {|response| .... }
|
||||||
リクエストオブジェクト request を送信します。POST の時は data も
|
HTTPResquest オブジェクト request を送信します。POST/PUT の時は data も
|
||||||
与えられます。(POST 以外で data を与えると ArgumentError を発生します)
|
与えられます (POST/PUT 以外で data を与えると ArgumentError を発生します)。
|
||||||
|
|
||||||
ブロックとともに呼びだされたときはボディを読みこまずに HTTPResponse
|
ブロックとともに呼びだされたときはボディを読みこまずに HTTPResponse
|
||||||
オブジェクトをブロックに与えます。
|
オブジェクトをブロックに与えます。
|
||||||
|
|
||||||
|
このメソッドは HTTP プロトコルに関連した例外は発生させません。
|
||||||
|
|
||||||
== class Net::HTTP::Get, Head, Post
|
== class Net::HTTP::Get, Head, Post
|
||||||
|
|
||||||
HTTP リクエストを抽象化するクラス。key はすべて大文字小文字を
|
HTTP リクエストを抽象化するクラス。key はすべて大文字小文字を
|
||||||
|
@ -76,17 +76,25 @@ each
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=== Hello ドメイン
|
=== HELO ドメイン
|
||||||
|
|
||||||
SMTP ではメールを送る側のホストの名前を要求されるのですが、
|
SMTP ではメールを送る側のホストの名前 (HELO ドメインと呼ぶ) を要求
|
||||||
ダイヤルアップなどの場合には自分のマシンに正式な名前がない場合が
|
されるのですが、Net::SMTP ではとりあえず localhost.localdomain と
|
||||||
あります。そのような場合は適宜 SMTP サーバの名前などを与えてやら
|
いう名前を送信しています。たいていの SMTP サーバはこの HELO ドメイン
|
||||||
ないと配送を拒否されることがあります。SMTP.start あるいは SMTP#start
|
による認証はあまり真面目に行わないので (簡単に偽造できるからです)
|
||||||
の引数 helo_domain がそれです。
|
問題にならないことが多いのですが、まれにメールセッションを切られる
|
||||||
|
こともあります。そういうときはとりあえず HELO ドメインを与えてみて
|
||||||
|
ください。もちろんそれ以外の時も HELO ドメインはちゃんと渡すのが
|
||||||
|
ベストです。
|
||||||
|
|
||||||
|
HELO ドメインは SMTP.start/SMTP#start の第三引数 helo_domain に指定
|
||||||
|
します。
|
||||||
|
|
||||||
Net::SMTP.start( 'your.smtp.server', 25,
|
Net::SMTP.start( 'your.smtp.server', 25,
|
||||||
'mail.from.domain' ) {|smtp|
|
'mail.from.domain' ) {|smtp|
|
||||||
|
|
||||||
|
よくあるダイヤルアップホストの場合、HELO ドメインには ISP のメール
|
||||||
|
サーバのドメインを使っておけばたいてい通ります。
|
||||||
|
|
||||||
== class Net::SMTP
|
== class Net::SMTP
|
||||||
|
|
||||||
@ -96,8 +104,8 @@ SMTP
|
|||||||
新しい SMTP オブジェクトを生成します。address はSMTPサーバーのFQDNで、
|
新しい SMTP オブジェクトを生成します。address はSMTPサーバーのFQDNで、
|
||||||
port は接続するポート番号です。ただし、このメソッドではまだ接続はしません。
|
port は接続するポート番号です。ただし、このメソッドではまだ接続はしません。
|
||||||
|
|
||||||
: start( address, port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil )
|
: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil )
|
||||||
: start( address, port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... }
|
: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil ) {|smtp| .... }
|
||||||
以下と同じです。
|
以下と同じです。
|
||||||
Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
|
Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
|
||||||
|
|
||||||
@ -161,11 +169,13 @@ SMTP
|
|||||||
|
|
||||||
# example
|
# example
|
||||||
Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
|
Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
|
||||||
smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) do |adapter|
|
smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) {|f|
|
||||||
adapter.write str1
|
f.puts 'From: aamine@loveruby.net'
|
||||||
adapter.write str2
|
f.puts 'To: someone@somedomain.org'
|
||||||
adapter.write str3
|
f.puts 'Subject: test mail'
|
||||||
end
|
f.puts
|
||||||
|
f.puts 'This is test mail.'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
== 発生する例外
|
== 発生する例外
|
||||||
|
325
lib/net/http.rb
325
lib/net/http.rb
@ -215,21 +215,22 @@ Yes, this is not thread-safe.
|
|||||||
: proxy_port
|
: proxy_port
|
||||||
port number of proxy host. If self does not use a proxy, nil.
|
port number of proxy host. If self does not use a proxy, nil.
|
||||||
|
|
||||||
: get( path, header = nil, dest = '' )
|
: get( path, header = nil )
|
||||||
: get( path, header = nil ) {|str| .... }
|
: get( path, header = nil ) {|str| .... }
|
||||||
gets data from PATH on the connecting host.
|
gets data from PATH on the connecting host.
|
||||||
HEADER must be a Hash like { 'Accept' => '*/*', ... }.
|
HEADER must be a Hash like { 'Accept' => '*/*', ... }.
|
||||||
Response body is written into DEST by using "<<" method.
|
|
||||||
|
|
||||||
This method returns Net::HTTPResponse object.
|
In version 1.1, this method returns a pair of objects,
|
||||||
|
a Net::HTTPResponse object and entity body string.
|
||||||
|
In version 1.2, this method returns a Net::HTTPResponse
|
||||||
|
object.
|
||||||
|
|
||||||
If called with block, gives entity body little by little
|
If called with block, gives entity body string to the block
|
||||||
to the block (as String).
|
little by little.
|
||||||
|
|
||||||
In version 1.1, this method might raises exception for also
|
In version 1.1, this method might raises exception for also
|
||||||
3xx (redirect). On the case you can get a HTTPResponse object
|
3xx (redirect). On the case you can get a HTTPResponse object
|
||||||
by "anException.response".
|
by "anException.response".
|
||||||
|
|
||||||
In version 1.2, this method never raises exception.
|
In version 1.2, this method never raises exception.
|
||||||
|
|
||||||
# version 1.1 (bundled with Ruby 1.6)
|
# version 1.1 (bundled with Ruby 1.6)
|
||||||
@ -248,10 +249,6 @@ Yes, this is not thread-safe.
|
|||||||
f.write str
|
f.write str
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
# same effect
|
|
||||||
File.open( 'save.txt', 'w' ) {|f|
|
|
||||||
http.get '/~foo/', nil, f
|
|
||||||
}
|
|
||||||
|
|
||||||
: head( path, header = nil )
|
: head( path, header = nil )
|
||||||
gets only header from PATH on the connecting host.
|
gets only header from PATH on the connecting host.
|
||||||
@ -262,6 +259,7 @@ Yes, this is not thread-safe.
|
|||||||
In version 1.1, this method might raises exception for also
|
In version 1.1, this method might raises exception for also
|
||||||
3xx (redirect). On the case you can get a HTTPResponse object
|
3xx (redirect). On the case you can get a HTTPResponse object
|
||||||
by "anException.response".
|
by "anException.response".
|
||||||
|
In version 1.2, this method never raises exception.
|
||||||
|
|
||||||
response = nil
|
response = nil
|
||||||
Net::HTTP.start( 'some.www.server', 80 ) {|http|
|
Net::HTTP.start( 'some.www.server', 80 ) {|http|
|
||||||
@ -269,67 +267,70 @@ Yes, this is not thread-safe.
|
|||||||
}
|
}
|
||||||
p response['content-type']
|
p response['content-type']
|
||||||
|
|
||||||
: post( path, data, header = nil, dest = '' )
|
: post( path, data, header = nil )
|
||||||
: post( path, data, header = nil ) {|str| .... }
|
: post( path, data, header = nil ) {|str| .... }
|
||||||
posts "data" (must be String) to "path".
|
posts DATA (must be String) to PATH. HEADER must be a Hash
|
||||||
If the body exists, also gets entity body.
|
like { 'Accept' => '*/*', ... }.
|
||||||
Response body is written into "dest" by using "<<" method.
|
|
||||||
"header" must be a Hash like { 'Accept' => '*/*', ... }.
|
In version 1.1, this method returns a pair of objects, a
|
||||||
This method returns Net::HTTPResponse object.
|
Net::HTTPResponse object and an entity body string.
|
||||||
|
In version 1.2, this method returns a Net::HTTPReponse object.
|
||||||
|
|
||||||
If called with block, gives a part of entity body string.
|
If called with block, gives a part of entity body string.
|
||||||
|
|
||||||
In version 1.1, this method might raises exception for also
|
In version 1.1, this method might raises exception for also
|
||||||
3xx (redirect). On the case you can get a HTTPResponse object
|
3xx (redirect). On the case you can get a HTTPResponse object
|
||||||
by "anException.response".
|
by "anException.response".
|
||||||
|
In version 1.2, this method never raises exception.
|
||||||
|
|
||||||
# version 1.1
|
# version 1.1
|
||||||
response, body = http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' )
|
response, body = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
||||||
|
|
||||||
# version 1.2
|
# version 1.2
|
||||||
response = http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' )
|
response = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
||||||
# compatible for both version
|
|
||||||
response , = http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' )
|
# compatible in both version
|
||||||
|
response , = http.post( '/cgi-bin/search.rb', 'query=subject&target=ruby' )
|
||||||
|
|
||||||
# using block
|
# using block
|
||||||
File.open( 'save.html', 'w' ) {|f|
|
File.open( 'save.html', 'w' ) {|f|
|
||||||
http.post( '/cgi-bin/search.rb', 'querytype=subject&target=ruby' ) do |str|
|
http.post( '/cgi-bin/search.rb',
|
||||||
|
'query=subject&target=ruby' ) do |str|
|
||||||
f.write str
|
f.write str
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
# same effect
|
|
||||||
File.open( 'save.html', 'w' ) {|f|
|
|
||||||
http.post '/cgi-bin/search.rb', 'querytype=subject&target=ruby', nil, f
|
|
||||||
}
|
|
||||||
|
|
||||||
: get2( path, header = nil )
|
: request_get( path, header = nil )
|
||||||
: get2( path, header = nil ) {|response| .... }
|
: request_get( path, header = nil ) {|response| .... }
|
||||||
gets entity from PATH. This method returns a HTTPResponse object.
|
gets entity from PATH. This method returns a HTTPResponse object.
|
||||||
|
|
||||||
When called with block, keep connection while block is executed
|
When called with block, keep connection while block is executed
|
||||||
and gives a HTTPResponse object to the block.
|
and gives a HTTPResponse object to the block.
|
||||||
|
|
||||||
This method never raise any ProtocolErrors.
|
This method never raises Net::* exceptions.
|
||||||
|
|
||||||
# example
|
# example
|
||||||
response = http.get2( '/index.html' )
|
response = http.request_get( '/index.html' )
|
||||||
p response['content-type']
|
p response['content-type']
|
||||||
puts response.body # body is already read
|
puts response.body # body is already read
|
||||||
|
|
||||||
# using block
|
# using block
|
||||||
http.get2( '/index.html' ) {|response|
|
http.request_get( '/index.html' ) {|response|
|
||||||
p response['content-type']
|
p response['content-type']
|
||||||
response.read_body do |str| # read body now
|
response.read_body do |str| # read body now
|
||||||
print str
|
print str
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
: post2( path, header = nil )
|
: request_post( path, data, header = nil )
|
||||||
: post2( path, header = nil ) {|response| .... }
|
: request_post( path, data, header = nil ) {|response| .... }
|
||||||
posts data to PATH. This method returns a HTTPResponse object.
|
posts data to PATH. This method returns a HTTPResponse object.
|
||||||
|
|
||||||
When called with block, gives a HTTPResponse object to the block
|
When called with block, gives a HTTPResponse object to the block
|
||||||
before reading entity body, with keeping connection.
|
before reading entity body, with keeping connection.
|
||||||
|
|
||||||
|
This method never raises Net::* exceptions.
|
||||||
|
|
||||||
# example
|
# example
|
||||||
response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
|
response = http.post2( '/cgi-bin/nice.rb', 'datadatadata...' )
|
||||||
p response.status
|
p response.status
|
||||||
@ -346,12 +347,14 @@ Yes, this is not thread-safe.
|
|||||||
|
|
||||||
: request( request [, data] )
|
: request( request [, data] )
|
||||||
: request( request [, data] ) {|response| .... }
|
: request( request [, data] ) {|response| .... }
|
||||||
sends a HTTPRequest object REQUEST to the (remote) http server.
|
sends a HTTPRequest object REQUEST to the HTTP server.
|
||||||
This method also writes DATA string if REQUEST is a post/put request.
|
This method also writes DATA string if REQUEST is a post/put request.
|
||||||
Giving DATA for get/head request causes ArgumentError.
|
Giving DATA for get/head request causes ArgumentError.
|
||||||
|
|
||||||
If called with block, passes a HTTPResponse object to the block
|
If called with block, this method passes a HTTPResponse object to
|
||||||
before reading entity body.
|
the block, without reading entity body.
|
||||||
|
|
||||||
|
This method never raises Net::* exceptions.
|
||||||
|
|
||||||
== class Net::HTTP::Get, Head, Post
|
== class Net::HTTP::Get, Head, Post
|
||||||
|
|
||||||
@ -460,17 +463,39 @@ module Net
|
|||||||
|
|
||||||
def initialize( addr, port = nil )
|
def initialize( addr, port = nil )
|
||||||
super
|
super
|
||||||
|
|
||||||
@curr_http_version = HTTPVersion
|
@curr_http_version = HTTPVersion
|
||||||
@seems_1_0_server = false
|
@seems_1_0_server = false
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def conn_command( sock )
|
def do_start
|
||||||
|
conn_socket
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_finish
|
def do_finish
|
||||||
|
disconn_socket
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# short cut methods
|
||||||
|
#
|
||||||
|
|
||||||
|
def HTTP.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 HTTP.get_print( addr, path, port = nil )
|
||||||
|
new( addr, port || HTTP.port ).start {|http|
|
||||||
|
http.get path, nil, $stdout
|
||||||
|
}
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -480,7 +505,6 @@ module Net
|
|||||||
|
|
||||||
public
|
public
|
||||||
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def Proxy( p_addr, p_port = nil )
|
def Proxy( p_addr, p_port = nil )
|
||||||
@ -496,7 +520,7 @@ module Net
|
|||||||
def new( address, port = nil, p_addr = nil, p_port = nil )
|
def new( address, port = nil, p_addr = nil, p_port = nil )
|
||||||
c = p_addr ? self::Proxy(p_addr, p_port) : self
|
c = p_addr ? self::Proxy(p_addr, p_port) : self
|
||||||
i = c.orig_new( address, port )
|
i = c.orig_new( address, port )
|
||||||
setvar i
|
setimplversion i
|
||||||
i
|
i
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -543,22 +567,26 @@ module Net
|
|||||||
mod = self
|
mod = self
|
||||||
klass = Class.new( HTTP )
|
klass = Class.new( HTTP )
|
||||||
klass.module_eval {
|
klass.module_eval {
|
||||||
include mod
|
include mod
|
||||||
@is_proxy_class = true
|
@is_proxy_class = true
|
||||||
@proxy_address = p_addr
|
@proxy_address = p_addr
|
||||||
@proxy_port = p_port
|
@proxy_port = p_port
|
||||||
}
|
}
|
||||||
klass
|
klass
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def conn_socket( addr, port )
|
def conn_address
|
||||||
super proxy_address, proxy_port
|
proxy_address()
|
||||||
|
end
|
||||||
|
|
||||||
|
def conn_port
|
||||||
|
proxy_port()
|
||||||
end
|
end
|
||||||
|
|
||||||
def edit_path( path )
|
def edit_path( path )
|
||||||
'http://' + addr_port + path
|
'http://' + addr_port() + path
|
||||||
end
|
end
|
||||||
|
|
||||||
end # module ProxyMod
|
end # module ProxyMod
|
||||||
@ -590,7 +618,7 @@ module Net
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def setvar( obj )
|
def setimplversion( obj )
|
||||||
f = @@newimpl
|
f = @@newimpl
|
||||||
obj.instance_eval { @newimpl = f }
|
obj.instance_eval { @newimpl = f }
|
||||||
end
|
end
|
||||||
@ -604,71 +632,92 @@ module Net
|
|||||||
|
|
||||||
public
|
public
|
||||||
|
|
||||||
def self.define_http_method_interface( nm, hasdest, hasdata )
|
def get( path, initheader = nil, dest = nil, &block )
|
||||||
name = nm.id2name.downcase
|
res = nil
|
||||||
cname = nm.id2name
|
request( Get.new(path,initheader) ) {|res|
|
||||||
lineno = __LINE__ + 2
|
res.read_body dest, &block
|
||||||
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 #{name}2( path, #{hasdata ? 'data,' : ''}
|
|
||||||
u_header = nil, &block )
|
|
||||||
request( #{cname}.new(path, u_header),
|
|
||||||
#{hasdata ? 'data,' : ''} &block )
|
|
||||||
end
|
|
||||||
----
|
|
||||||
module_eval src, __FILE__, lineno
|
|
||||||
end
|
|
||||||
|
|
||||||
define_http_method_interface :Get, true, false
|
|
||||||
define_http_method_interface :Head, false, false
|
|
||||||
define_http_method_interface :Post, true, true
|
|
||||||
define_http_method_interface :Put, false, true
|
|
||||||
|
|
||||||
def request( req, body = nil, &block )
|
|
||||||
unless active? then
|
|
||||||
start {
|
|
||||||
req['connection'] = 'close'
|
|
||||||
return request(req, body, &block)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
connecting( req ) {
|
|
||||||
req.__send__( :exec,
|
|
||||||
@socket, @curr_http_version, edit_path(req.path), body )
|
|
||||||
yield req.response if block_given?
|
|
||||||
}
|
}
|
||||||
req.response
|
unless @newimpl then
|
||||||
|
res.value
|
||||||
|
return res, res.body
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def head( path, initheader = nil )
|
||||||
|
res = request( Head.new(path,initheader) )
|
||||||
|
@newimpl or res.value
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def post( path, data, initheader = nil, dest = nil, &block )
|
||||||
|
res = nil
|
||||||
|
request( Post.new(path,initheader), data ) {|res|
|
||||||
|
res.read_body dest, &block
|
||||||
|
}
|
||||||
|
unless @newimpl then
|
||||||
|
res.value
|
||||||
|
return res, res.body
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def put( path, data, initheader = nil )
|
||||||
|
res = request( Put.new(path,initheader), data )
|
||||||
|
@newimpl or res.value
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_get( path, initheader = nil, &block )
|
||||||
|
request Get.new(path,initheader), &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_head( path, initheader = nil, &block )
|
||||||
|
request Head.new(path,initheader), &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_post( path, data, initheader = nil, &block )
|
||||||
|
request Post.new(path,initheader), data, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def request_put( path, data, initheader = nil, &block )
|
||||||
|
request Put.new(path,initheader), data, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
alias get2 request_get
|
||||||
|
alias head2 request_head
|
||||||
|
alias post2 request_post
|
||||||
|
alias put2 request_put
|
||||||
|
|
||||||
def send_request( name, path, body = nil, header = nil )
|
def send_request( name, path, body = nil, header = nil )
|
||||||
r = HTTPGenericRequest.new( name, (body ? true : false), true,
|
r = HTTPGenericRequest.new( name, (body ? true : false), true,
|
||||||
path, header )
|
path, header )
|
||||||
request r, body
|
request r, body
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def request( req, body = nil, &block )
|
||||||
|
unless active? then
|
||||||
|
start {
|
||||||
|
req['connection'] = 'close'
|
||||||
|
return request(req, body, &block)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
connecting( req ) {
|
||||||
|
req.__send__( :exec,
|
||||||
|
@socket, @curr_http_version, edit_path(req.path), body )
|
||||||
|
yield req.response if block_given?
|
||||||
|
}
|
||||||
|
req.response
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
||||||
def connecting( req )
|
def connecting( req )
|
||||||
if @socket.closed? then
|
if @socket.closed? then
|
||||||
re_connect
|
reconn_socket
|
||||||
end
|
end
|
||||||
if not req.body_exist? or @seems_1_0_server then
|
if not req.body_exist? or @seems_1_0_server then
|
||||||
req['connection'] = 'close'
|
req['connection'] = 'close'
|
||||||
@ -712,25 +761,6 @@ module Net
|
|||||||
# utils
|
# utils
|
||||||
#
|
#
|
||||||
|
|
||||||
public
|
|
||||||
|
|
||||||
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 self.get_print( addr, path, port = nil )
|
|
||||||
new( addr, port || HTTP.port ).start {|http|
|
|
||||||
http.get path, nil, $stdout
|
|
||||||
}
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def addr_port
|
def addr_port
|
||||||
@ -746,8 +776,6 @@ module Net
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
HTTPSession = HTTP
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Code
|
class Code
|
||||||
@ -894,9 +922,7 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
def range=( r, fin = nil )
|
def range=( r, fin = nil )
|
||||||
if fin then
|
r = (r ... r + fin) if fin
|
||||||
r = r ... r+fin
|
|
||||||
end
|
|
||||||
|
|
||||||
case r
|
case r
|
||||||
when Numeric
|
when Numeric
|
||||||
@ -970,7 +996,7 @@ module Net
|
|||||||
|
|
||||||
include HTTPHeader
|
include HTTPHeader
|
||||||
|
|
||||||
def initialize( m, reqbody, resbody, path, uhead = nil )
|
def initialize( m, reqbody, resbody, path, initheader = nil )
|
||||||
@method = m
|
@method = m
|
||||||
@request_has_body = reqbody
|
@request_has_body = reqbody
|
||||||
@response_has_body = resbody
|
@response_has_body = resbody
|
||||||
@ -978,8 +1004,8 @@ module Net
|
|||||||
@response = nil
|
@response = nil
|
||||||
|
|
||||||
@header = tmp = {}
|
@header = tmp = {}
|
||||||
return unless uhead
|
return unless initheader
|
||||||
uhead.each do |k,v|
|
initheader.each do |k,v|
|
||||||
key = k.downcase
|
key = k.downcase
|
||||||
if tmp.key? key then
|
if tmp.key? key then
|
||||||
$stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
|
$stderr.puts "WARNING: duplicated HTTP header: #{k}" if $VERBOSE
|
||||||
@ -1023,7 +1049,7 @@ module Net
|
|||||||
check_arg_n body
|
check_arg_n body
|
||||||
sendreq_no_body sock, ver, path
|
sendreq_no_body sock, ver, path
|
||||||
end
|
end
|
||||||
@response = r = get_response( sock )
|
@response = r = get_response(sock)
|
||||||
r
|
r
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1033,12 +1059,8 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
def check_arg_b( data, block )
|
def check_arg_b( data, block )
|
||||||
if data and block then
|
(data and block) and raise ArgumentError, 'both of data and block given'
|
||||||
raise ArgumentError, 'both of data and block given'
|
(data or block) or raise ArgumentError, 'str or block required'
|
||||||
end
|
|
||||||
unless data or block then
|
|
||||||
raise ArgumentError, 'str or block required'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_arg_n( data )
|
def check_arg_n( data )
|
||||||
@ -1094,11 +1116,11 @@ module Net
|
|||||||
|
|
||||||
class HTTPRequest < HTTPGenericRequest
|
class HTTPRequest < HTTPGenericRequest
|
||||||
|
|
||||||
def initialize( path, uhead = nil )
|
def initialize( path, initheader = nil )
|
||||||
super type::METHOD,
|
super type::METHOD,
|
||||||
type::REQUEST_HAS_BODY,
|
type::REQUEST_HAS_BODY,
|
||||||
type::RESPONSE_HAS_BODY,
|
type::RESPONSE_HAS_BODY,
|
||||||
path, uhead
|
path, initheader
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -1226,7 +1248,7 @@ module Net
|
|||||||
|
|
||||||
while true do
|
while true do
|
||||||
line = sock.readuntil( "\n", true ) # ignore EOF
|
line = sock.readuntil( "\n", true ) # ignore EOF
|
||||||
line.sub!( /\s+\z/, '' ) # don't use chop!
|
line.sub!( /\s+\z/, '' ) # don't use chop!
|
||||||
break if line.empty?
|
break if line.empty?
|
||||||
|
|
||||||
m = /\A([^:]+):\s*/.match( line )
|
m = /\A([^:]+):\s*/.match( line )
|
||||||
@ -1298,22 +1320,22 @@ module Net
|
|||||||
#
|
#
|
||||||
|
|
||||||
def read_body( dest = nil, &block )
|
def read_body( dest = nil, &block )
|
||||||
if @read and (dest or block) then
|
if @read then
|
||||||
raise IOError, "#{type}\#read_body called twice with argument"
|
(dest or block) and
|
||||||
|
raise IOError, "#{type}\#read_body called twice with argument"
|
||||||
|
return @body
|
||||||
end
|
end
|
||||||
|
|
||||||
unless @read then
|
to = procdest(dest, block)
|
||||||
to = procdest( dest, block )
|
stream_check
|
||||||
stream_check
|
|
||||||
|
|
||||||
if @body_exist and code_type.body_exist? then
|
if @body_exist and code_type.body_exist? then
|
||||||
read_body_0 to
|
read_body_0 to
|
||||||
@body = to
|
@body = to
|
||||||
else
|
else
|
||||||
@body = nil
|
@body = nil
|
||||||
end
|
|
||||||
@read = true
|
|
||||||
end
|
end
|
||||||
|
@read = true
|
||||||
|
|
||||||
@body
|
@body
|
||||||
end
|
end
|
||||||
@ -1321,10 +1343,8 @@ module Net
|
|||||||
alias body read_body
|
alias body read_body
|
||||||
alias entity read_body
|
alias entity read_body
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
||||||
def terminate
|
def terminate
|
||||||
read_body
|
read_body
|
||||||
end
|
end
|
||||||
@ -1353,11 +1373,11 @@ module Net
|
|||||||
|
|
||||||
while true do
|
while true do
|
||||||
line = @socket.readline
|
line = @socket.readline
|
||||||
m = /[0-9a-fA-F]+/.match( line )
|
m = /[0-9a-fA-F]+/.match(line)
|
||||||
m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
m or raise HTTPBadResponse, "wrong chunk size line: #{line}"
|
||||||
len = m[0].hex
|
len = m[0].hex
|
||||||
break if len == 0
|
break if len == 0
|
||||||
@socket.read( len, dest ); total += len
|
@socket.read len, dest; total += len
|
||||||
@socket.read 2 # \r\n
|
@socket.read 2 # \r\n
|
||||||
end
|
end
|
||||||
until @socket.readline.empty? do
|
until @socket.readline.empty? do
|
||||||
@ -1384,6 +1404,9 @@ module Net
|
|||||||
|
|
||||||
|
|
||||||
# for backward compatibility
|
# for backward compatibility
|
||||||
|
|
||||||
|
HTTPSession = HTTP
|
||||||
|
|
||||||
module NetPrivate
|
module NetPrivate
|
||||||
HTTPResponse = ::Net::HTTPResponse
|
HTTPResponse = ::Net::HTTPResponse
|
||||||
HTTPGenericRequest = ::Net::HTTPGenericRequest
|
HTTPGenericRequest = ::Net::HTTPGenericRequest
|
||||||
|
178
lib/net/pop.rb
178
lib/net/pop.rb
@ -367,86 +367,96 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def auth_only( account, password )
|
||||||
|
active? and raise IOError, 'opening already opened POP session'
|
||||||
|
start( account, password ) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# connection
|
||||||
|
#
|
||||||
|
|
||||||
def initialize( addr, port = nil, apop = false )
|
def initialize( addr, port = nil, apop = false )
|
||||||
super addr, port
|
super addr, port
|
||||||
@mails = nil
|
@mails = nil
|
||||||
@apop = false
|
@apop = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def auth_only( account, password )
|
private
|
||||||
begin
|
|
||||||
connect
|
def do_start( account, password )
|
||||||
@active = true
|
conn_socket
|
||||||
@command.auth address(), port()
|
@command = (@apop ? type.apop_command_type : type.command_type).new(socket())
|
||||||
@command.quit
|
@command.auth account, password
|
||||||
ensure
|
|
||||||
@active = false
|
|
||||||
disconnect
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
attr :mails
|
def do_finish
|
||||||
|
@mails = nil
|
||||||
|
disconn_command
|
||||||
|
disconn_socket
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# POP operations
|
||||||
|
#
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def mails
|
||||||
|
return @mails if @mails
|
||||||
|
|
||||||
|
mails = []
|
||||||
|
mtype = type.mail_type
|
||||||
|
command().list.each_with_index do |size,idx|
|
||||||
|
mails.push mtype.new(idx, size, command()) if size
|
||||||
|
end
|
||||||
|
@mails = mails.freeze
|
||||||
|
end
|
||||||
|
|
||||||
def each_mail( &block )
|
def each_mail( &block )
|
||||||
io_check
|
mails().each( &block )
|
||||||
@mails.each( &block )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
alias each each_mail
|
alias each each_mail
|
||||||
|
|
||||||
def delete_all
|
def delete_all
|
||||||
io_check
|
mails().each do |m|
|
||||||
@mails.each do |m|
|
|
||||||
yield m if block_given?
|
yield m if block_given?
|
||||||
m.delete unless m.deleted?
|
m.delete unless m.deleted?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset
|
def reset
|
||||||
io_check
|
command().rset
|
||||||
@command.rset
|
mails().each do |m|
|
||||||
@mails.each do |m|
|
|
||||||
m.instance_eval { @deleted = false }
|
m.instance_eval { @deleted = false }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
def command
|
||||||
|
io_check
|
||||||
def conn_command( sock )
|
super
|
||||||
@command =
|
|
||||||
(@apop ? type.apop_command_type : type.command_type).new(sock)
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_start( account, password )
|
|
||||||
@command.auth account, password
|
|
||||||
|
|
||||||
mails = []
|
|
||||||
mtype = type.mail_type
|
|
||||||
@command.list.each_with_index do |size,idx|
|
|
||||||
mails.push mtype.new(idx, size, @command) if size
|
|
||||||
end
|
|
||||||
@mails = mails.freeze
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def io_check
|
def io_check
|
||||||
(not @socket or @socket.closed?) and
|
(not socket() or socket().closed?) and
|
||||||
raise IOError, 'pop session is not opened yet'
|
raise IOError, 'POP session is not opened yet'
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
POP = POP3
|
POP = POP3
|
||||||
POPSession = POP3
|
|
||||||
POP3Session = POP3
|
|
||||||
|
|
||||||
|
|
||||||
class APOP < POP3
|
class APOP < POP3
|
||||||
protocol_param :command_type, '::Net::APOPCommand'
|
protocol_param :command_type, '::Net::APOPCommand'
|
||||||
end
|
end
|
||||||
|
|
||||||
APOPSession = APOP
|
|
||||||
|
|
||||||
|
|
||||||
class POPMail
|
class POPMail
|
||||||
|
|
||||||
@ -500,86 +510,84 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class POP3Command < Command
|
class POP3Command < Command
|
||||||
|
|
||||||
def initialize( sock )
|
def initialize( sock )
|
||||||
super
|
super
|
||||||
critical {
|
atomic {
|
||||||
check_reply SuccessCode
|
check_reply SuccessCode
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def auth( account, pass )
|
def auth( account, pass )
|
||||||
critical {
|
atomic {
|
||||||
@socket.writeline 'USER ' + account
|
@socket.writeline 'USER ' + account
|
||||||
check_reply_auth
|
check_reply_auth
|
||||||
|
|
||||||
@socket.writeline 'PASS ' + pass
|
@socket.writeline 'PASS ' + pass
|
||||||
check_reply_auth
|
check_reply_auth
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def list
|
def list
|
||||||
arr = []
|
arr = []
|
||||||
critical {
|
atomic {
|
||||||
getok 'LIST'
|
getok 'LIST'
|
||||||
@socket.read_pendlist do |line|
|
@socket.read_pendlist do |line|
|
||||||
m = /\A(\d+)[ \t]+(\d+)/.match(line) or
|
m = /\A(\d+)[ \t]+(\d+)/.match(line) or
|
||||||
raise BadResponse, "illegal response: #{line}"
|
raise BadResponse, "illegal response: #{line}"
|
||||||
arr[ m[1].to_i ] = m[2].to_i
|
arr[ m[1].to_i ] = m[2].to_i
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
arr
|
arr
|
||||||
end
|
end
|
||||||
|
|
||||||
def rset
|
def rset
|
||||||
critical {
|
atomic {
|
||||||
getok 'RSET'
|
getok 'RSET'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def top( num, lines = 0, dest = '' )
|
def top( num, lines = 0, dest = '' )
|
||||||
critical {
|
atomic {
|
||||||
getok sprintf( 'TOP %d %d', num, lines )
|
getok sprintf( 'TOP %d %d', num, lines )
|
||||||
@socket.read_pendstr dest
|
@socket.read_pendstr dest
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def retr( num, dest = '', &block )
|
def retr( num, dest = '', &block )
|
||||||
critical {
|
atomic {
|
||||||
getok sprintf('RETR %d', num)
|
getok sprintf('RETR %d', num)
|
||||||
@socket.read_pendstr dest, &block
|
@socket.read_pendstr dest, &block
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def dele( num )
|
def dele( num )
|
||||||
critical {
|
atomic {
|
||||||
getok sprintf('DELE %d', num)
|
getok sprintf('DELE %d', num)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def uidl( num )
|
def uidl( num )
|
||||||
critical {
|
atomic {
|
||||||
getok( sprintf('UIDL %d', num) ).msg.split(' ')[1]
|
getok( sprintf('UIDL %d', num) ).msg.split(' ')[1]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def quit
|
def quit
|
||||||
critical {
|
atomic {
|
||||||
getok 'QUIT'
|
getok 'QUIT'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def check_reply_auth
|
def check_reply_auth
|
||||||
begin
|
begin
|
||||||
return check_reply( SuccessCode )
|
return check_reply(SuccessCode)
|
||||||
rescue ProtocolError => err
|
rescue ProtocolError => err
|
||||||
raise ProtoAuthError.new( 'Fail to POP authentication', err.response )
|
raise ProtoAuthError.new('Fail to POP authentication', err.response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -599,22 +607,28 @@ module Net
|
|||||||
class APOPCommand < POP3Command
|
class APOPCommand < POP3Command
|
||||||
|
|
||||||
def initialize( sock )
|
def initialize( sock )
|
||||||
rep = super( sock )
|
response = super(sock)
|
||||||
|
m = /<.+>/.match(response.msg) or
|
||||||
m = /<.+>/.match( rep.msg ) or
|
raise ProtoAuthError.new("not APOP server: cannot login", nil)
|
||||||
raise ProtoAuthError.new( "not APOP server: cannot login", nil )
|
|
||||||
@stamp = m[0]
|
@stamp = m[0]
|
||||||
end
|
end
|
||||||
|
|
||||||
def auth( account, pass )
|
def auth( account, pass )
|
||||||
critical {
|
atomic {
|
||||||
@socket.writeline sprintf( 'APOP %s %s',
|
@socket.writeline sprintf('APOP %s %s',
|
||||||
account,
|
account,
|
||||||
Digest::MD5.hexdigest(@stamp + pass) )
|
Digest::MD5.hexdigest(@stamp + pass))
|
||||||
check_reply_auth
|
check_reply_auth
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# for backward compatibility
|
||||||
|
|
||||||
|
POPSession = POP3
|
||||||
|
POP3Session = POP3
|
||||||
|
APOPSession = APOP
|
||||||
|
|
||||||
end # module Net
|
end # module Net
|
||||||
|
@ -28,27 +28,17 @@ module Net
|
|||||||
Version = '1.2.3'
|
Version = '1.2.3'
|
||||||
Revision = %q$Revision$.split(/\s+/)[1]
|
Revision = %q$Revision$.split(/\s+/)[1]
|
||||||
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
def start( address, port = nil, *args )
|
|
||||||
instance = new( address, port )
|
|
||||||
|
|
||||||
if block_given? then
|
|
||||||
instance.start( *args ) { yield instance }
|
|
||||||
else
|
|
||||||
instance.start( *args )
|
|
||||||
instance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def protocol_param( name, val )
|
def protocol_param( name, val )
|
||||||
module_eval %-
|
module_eval <<-End, __FILE__, __LINE__ + 1
|
||||||
def self.#{name.id2name}
|
def self.#{name.id2name}
|
||||||
#{val}
|
#{val}
|
||||||
end
|
end
|
||||||
-
|
End
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -61,11 +51,11 @@ module Net
|
|||||||
# protocol_param command_type
|
# protocol_param command_type
|
||||||
# protocol_param socket_type (optional)
|
# protocol_param socket_type (optional)
|
||||||
#
|
#
|
||||||
# private method do_start (optional)
|
# private method do_start
|
||||||
# private method do_finish (optional)
|
# private method do_finish
|
||||||
#
|
#
|
||||||
# private method on_connect (optional)
|
# private method conn_address
|
||||||
# private method on_disconnect (optional)
|
# private method conn_port
|
||||||
#
|
#
|
||||||
|
|
||||||
protocol_param :port, 'nil'
|
protocol_param :port, 'nil'
|
||||||
@ -73,6 +63,19 @@ module Net
|
|||||||
protocol_param :socket_type, '::Net::BufferedSocket'
|
protocol_param :socket_type, '::Net::BufferedSocket'
|
||||||
|
|
||||||
|
|
||||||
|
def Protocol.start( address, port = nil, *args )
|
||||||
|
instance = new( address, port )
|
||||||
|
|
||||||
|
if block_given? then
|
||||||
|
ret = nil
|
||||||
|
instance.start( *args ) { ret = yield(instance) }
|
||||||
|
ret
|
||||||
|
else
|
||||||
|
instance.start( *args )
|
||||||
|
instance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def initialize( addr, port = nil )
|
def initialize( addr, port = nil )
|
||||||
@address = addr
|
@address = addr
|
||||||
@port = port || type.port
|
@port = port || type.port
|
||||||
@ -82,8 +85,8 @@ module Net
|
|||||||
|
|
||||||
@active = false
|
@active = false
|
||||||
|
|
||||||
@open_timeout = nil
|
@open_timeout = 30
|
||||||
@read_timeout = nil
|
@read_timeout = 60
|
||||||
|
|
||||||
@dout = nil
|
@dout = nil
|
||||||
end
|
end
|
||||||
@ -112,90 +115,80 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# open session
|
# open
|
||||||
#
|
#
|
||||||
|
|
||||||
def start( *args )
|
def start( *args )
|
||||||
active? and raise IOError, 'protocol has been opened already'
|
@active and raise IOError, 'protocol has been opened already'
|
||||||
|
|
||||||
if block_given? then
|
if block_given? then
|
||||||
begin
|
begin
|
||||||
_start args
|
do_start( *args )
|
||||||
yield self
|
@active = true
|
||||||
|
return yield(self)
|
||||||
ensure
|
ensure
|
||||||
finish if active?
|
finish if @active
|
||||||
end
|
end
|
||||||
else
|
|
||||||
_start args
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do_start( *args )
|
||||||
|
@active = true
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def _start( args )
|
# abstract do_start()
|
||||||
connect
|
|
||||||
do_start( *args )
|
|
||||||
@active = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def connect
|
def conn_socket
|
||||||
conn_socket @address, @port
|
@socket = type.socket_type.open(
|
||||||
|
conn_address(), conn_port(),
|
||||||
|
@open_timeout, @read_timeout, @dout )
|
||||||
on_connect
|
on_connect
|
||||||
conn_command @socket
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def re_connect
|
alias conn_address address
|
||||||
|
alias conn_port port
|
||||||
|
|
||||||
|
def reconn_socket
|
||||||
@socket.reopen @open_timeout
|
@socket.reopen @open_timeout
|
||||||
on_connect
|
on_connect
|
||||||
end
|
end
|
||||||
|
|
||||||
def conn_socket( addr, port )
|
def conn_command
|
||||||
@socket = type.socket_type.open(
|
@command = type.command_type.new(@socket)
|
||||||
addr, port, @open_timeout, @read_timeout, @dout )
|
|
||||||
end
|
|
||||||
|
|
||||||
def conn_command( sock )
|
|
||||||
@command = type.command_type.new( sock )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def on_connect
|
def on_connect
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_start
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# close session
|
# close
|
||||||
#
|
#
|
||||||
|
|
||||||
public
|
public
|
||||||
|
|
||||||
def finish
|
def finish
|
||||||
active? or raise IOError, 'already closed protocol'
|
active? or raise IOError, 'closing already closed protocol'
|
||||||
|
do_finish
|
||||||
do_finish if @command and not @command.critical?
|
|
||||||
disconnect
|
|
||||||
@active = false
|
@active = false
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def do_finish
|
# abstract do_finish()
|
||||||
@command.quit
|
|
||||||
|
def disconn_command
|
||||||
|
@command.quit if @command and not @command.critical?
|
||||||
|
@command = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def disconnect
|
def disconn_socket
|
||||||
@command = nil
|
|
||||||
if @socket and not @socket.closed? then
|
if @socket and not @socket.closed? then
|
||||||
@socket.close
|
@socket.close
|
||||||
end
|
end
|
||||||
@socket = nil
|
@socket = nil
|
||||||
on_disconnect
|
|
||||||
end
|
|
||||||
|
|
||||||
def on_disconnect
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -220,7 +213,7 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
def error!
|
def error!
|
||||||
raise @code_type.error_type.new( code + ' ' + Net.quote(msg), self )
|
raise @code_type.error_type.new( code + ' ' + msg.dump, self )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -305,20 +298,31 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
def inspect
|
def inspect
|
||||||
"#<#{type}>"
|
"#<#{type} socket=#{@socket.inspect}>"
|
||||||
end
|
|
||||||
|
|
||||||
def write( str )
|
|
||||||
@socket.__send__ @mid, str
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def <<( str )
|
def <<( str )
|
||||||
@socket.__send__ @mid, str
|
@socket.__send__ @mid, str
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def write( str )
|
||||||
|
@socket.__send__ @mid, str
|
||||||
|
end
|
||||||
|
|
||||||
|
alias print write
|
||||||
|
|
||||||
|
def puts( str = '' )
|
||||||
|
@socket.__send__ @mid, str.sub(/\n?/, "\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
def printf( *args )
|
||||||
|
@socket.__send__ @mid, sprintf(*args)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
class ReadAdapter
|
class ReadAdapter
|
||||||
|
|
||||||
def initialize( block )
|
def initialize( block )
|
||||||
@ -330,25 +334,13 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
def <<( str )
|
def <<( str )
|
||||||
callblock( str, &@block ) if @block
|
call_block str, &@block if @block
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def callblock( str )
|
def call_block( str )
|
||||||
begin
|
yield str
|
||||||
user_break = true
|
|
||||||
yield str
|
|
||||||
user_break = false
|
|
||||||
rescue Exception
|
|
||||||
user_break = false
|
|
||||||
raise
|
|
||||||
ensure
|
|
||||||
if user_break then
|
|
||||||
@block = nil
|
|
||||||
return # stop breaking
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -360,7 +352,7 @@ module Net
|
|||||||
def initialize( sock )
|
def initialize( sock )
|
||||||
@socket = sock
|
@socket = sock
|
||||||
@last_reply = nil
|
@last_reply = nil
|
||||||
@critical = false
|
@atomic = false
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :socket
|
attr_accessor :socket
|
||||||
@ -370,23 +362,20 @@ module Net
|
|||||||
"#<#{type}>"
|
"#<#{type}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
# abstract quit
|
# abstract quit()
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# abstract get_reply()
|
|
||||||
|
|
||||||
def check_reply( *oks )
|
def check_reply( *oks )
|
||||||
@last_reply = get_reply
|
@last_reply = get_reply()
|
||||||
reply_must( @last_reply, *oks )
|
reply_must @last_reply, *oks
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# abstract get_reply()
|
||||||
|
|
||||||
def reply_must( rep, *oks )
|
def reply_must( rep, *oks )
|
||||||
oks.each do |i|
|
oks.each do |i|
|
||||||
if i === rep then
|
return rep if i === rep
|
||||||
return rep
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
rep.error!
|
rep.error!
|
||||||
end
|
end
|
||||||
@ -396,7 +385,6 @@ module Net
|
|||||||
check_reply expect
|
check_reply expect
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# error handle
|
# error handle
|
||||||
#
|
#
|
||||||
@ -404,80 +392,77 @@ module Net
|
|||||||
public
|
public
|
||||||
|
|
||||||
def critical?
|
def critical?
|
||||||
@critical
|
@atomic
|
||||||
end
|
end
|
||||||
|
|
||||||
def error_ok
|
def error_ok
|
||||||
@critical = false
|
@atomic = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def critical
|
def atomic
|
||||||
@critical = true
|
@atomic = true
|
||||||
ret = yield
|
ret = yield
|
||||||
@critical = false
|
@atomic = false
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def begin_critical
|
def begin_atomic
|
||||||
ret = @critical
|
ret = @atomic
|
||||||
@critical = true
|
@atomic = true
|
||||||
not ret
|
not ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def end_critical
|
def end_atomic
|
||||||
@critical = false
|
@atomic = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias critical atomic
|
||||||
|
alias begin_critical begin_atomic
|
||||||
|
alias end_critical end_atomic
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class BufferedSocket
|
class BufferedSocket
|
||||||
|
|
||||||
def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
|
|
||||||
@addr = addr
|
|
||||||
@port = port
|
|
||||||
|
|
||||||
@read_timeout = rtime
|
|
||||||
|
|
||||||
@debugout = dout
|
|
||||||
|
|
||||||
@socket = nil
|
|
||||||
@sending = ''
|
|
||||||
@rbuf = ''
|
|
||||||
|
|
||||||
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
|
class << self
|
||||||
alias open new
|
alias open new
|
||||||
end
|
end
|
||||||
|
|
||||||
def inspect
|
def initialize( addr, port, otime = nil, rtime = nil, dout = nil )
|
||||||
"#<#{type} #{closed? ? 'closed' : 'opened'}>"
|
@address = addr
|
||||||
end
|
@port = port
|
||||||
|
@read_timeout = rtime
|
||||||
|
@debugout = dout
|
||||||
|
|
||||||
|
@socket = nil
|
||||||
|
@rbuf = nil
|
||||||
|
|
||||||
def reopen( otime = nil )
|
|
||||||
D 'reopening...'
|
|
||||||
close
|
|
||||||
connect otime
|
connect otime
|
||||||
D 'reopened'
|
D 'opened'
|
||||||
end
|
end
|
||||||
|
|
||||||
attr :socket, true
|
attr_reader :address
|
||||||
|
attr_reader :port
|
||||||
|
|
||||||
|
def ip_address
|
||||||
|
@socket or return ''
|
||||||
|
@socket.addr[3]
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :socket
|
||||||
|
|
||||||
|
def connect( otime )
|
||||||
|
D "opening connection to #{@address}..."
|
||||||
|
timeout( otime ) {
|
||||||
|
@socket = TCPsocket.new( @address, @port )
|
||||||
|
}
|
||||||
|
@rbuf = ''
|
||||||
|
end
|
||||||
|
private :connect
|
||||||
|
|
||||||
def close
|
def close
|
||||||
if @socket then
|
if @socket then
|
||||||
@ -490,48 +475,43 @@ module Net
|
|||||||
@rbuf = ''
|
@rbuf = ''
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reopen( otime = nil )
|
||||||
|
D 'reopening...'
|
||||||
|
close
|
||||||
|
connect otime
|
||||||
|
D 'reopened'
|
||||||
|
end
|
||||||
|
|
||||||
def closed?
|
def closed?
|
||||||
not @socket
|
not @socket
|
||||||
end
|
end
|
||||||
|
|
||||||
def address
|
def inspect
|
||||||
@addr.dup
|
"#<#{type} #{closed? ? 'closed' : 'opened'}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
alias addr address
|
###
|
||||||
|
### READ
|
||||||
attr_reader :port
|
###
|
||||||
|
|
||||||
def ip_address
|
|
||||||
@socket or return ''
|
|
||||||
@socket.addr[3]
|
|
||||||
end
|
|
||||||
|
|
||||||
alias ipaddr ip_address
|
|
||||||
|
|
||||||
attr_reader :sending
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# input
|
# basic reader
|
||||||
#
|
#
|
||||||
|
|
||||||
public
|
public
|
||||||
|
|
||||||
CRLF = "\r\n"
|
def read( len, dest = '', ignore = false )
|
||||||
|
|
||||||
def read( len, dest = '', igneof = false )
|
|
||||||
D_off "reading #{len} bytes..."
|
D_off "reading #{len} bytes..."
|
||||||
|
|
||||||
rsize = 0
|
rsize = 0
|
||||||
begin
|
begin
|
||||||
while rsize + @rbuf.size < len do
|
while rsize + @rbuf.size < len do
|
||||||
rsize += rbuf_moveto( dest, @rbuf.size )
|
rsize += rbuf_moveto(dest, @rbuf.size)
|
||||||
rbuf_fill
|
rbuf_fill
|
||||||
end
|
end
|
||||||
rbuf_moveto dest, len - rsize
|
rbuf_moveto dest, len - rsize
|
||||||
rescue EOFError
|
rescue EOFError
|
||||||
raise unless igneof
|
raise unless ignore
|
||||||
end
|
end
|
||||||
|
|
||||||
D_on "read #{len} bytes"
|
D_on "read #{len} bytes"
|
||||||
@ -544,7 +524,7 @@ module Net
|
|||||||
rsize = 0
|
rsize = 0
|
||||||
begin
|
begin
|
||||||
while true do
|
while true do
|
||||||
rsize += rbuf_moveto( dest, @rbuf.size )
|
rsize += rbuf_moveto(dest, @rbuf.size)
|
||||||
rbuf_fill
|
rbuf_fill
|
||||||
end
|
end
|
||||||
rescue EOFError
|
rescue EOFError
|
||||||
@ -555,28 +535,34 @@ module Net
|
|||||||
dest
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
def readuntil( target, igneof = false )
|
def readuntil( target, ignore = false )
|
||||||
dest = ''
|
dest = ''
|
||||||
begin
|
begin
|
||||||
while true do
|
while true do
|
||||||
idx = @rbuf.index( target )
|
idx = @rbuf.index(target)
|
||||||
break if idx
|
break if idx
|
||||||
rbuf_fill
|
rbuf_fill
|
||||||
end
|
end
|
||||||
rbuf_moveto dest, idx + target.size
|
rbuf_moveto dest, idx + target.size
|
||||||
rescue EOFError
|
rescue EOFError
|
||||||
raise unless igneof
|
raise unless ignore
|
||||||
rbuf_moveto dest, @rbuf.size
|
rbuf_moveto dest, @rbuf.size
|
||||||
end
|
end
|
||||||
dest
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
def readline
|
def readline
|
||||||
ret = readuntil( "\n" )
|
ret = readuntil("\n")
|
||||||
ret.chop!
|
ret.chop!
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# line oriented reader
|
||||||
|
#
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
def read_pendstr( dest )
|
def read_pendstr( dest )
|
||||||
D_off 'reading text...'
|
D_off 'reading text...'
|
||||||
|
|
||||||
@ -590,7 +576,7 @@ module Net
|
|||||||
D_on "read #{rsize} bytes"
|
D_on "read #{rsize} bytes"
|
||||||
dest
|
dest
|
||||||
end
|
end
|
||||||
|
|
||||||
# private use only (can not handle 'break')
|
# private use only (can not handle 'break')
|
||||||
def read_pendlist
|
def read_pendlist
|
||||||
# D_off 'reading list...'
|
# D_off 'reading list...'
|
||||||
@ -606,6 +592,10 @@ module Net
|
|||||||
# D_on "read #{i} items"
|
# D_on "read #{i} items"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# lib (reader)
|
||||||
|
#
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
BLOCK_SIZE = 1024 * 2
|
BLOCK_SIZE = 1024 * 2
|
||||||
@ -623,50 +613,60 @@ module Net
|
|||||||
|
|
||||||
def rbuf_moveto( dest, len )
|
def rbuf_moveto( dest, len )
|
||||||
dest << (s = @rbuf.slice!(0, len))
|
dest << (s = @rbuf.slice!(0, len))
|
||||||
@debugout << %Q<read "#{Net.quote s}"\n> if @debugout
|
@debugout << %Q[-> #{s.dump}\n] if @debugout
|
||||||
len
|
len
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
### WRITE
|
||||||
|
###
|
||||||
|
|
||||||
#
|
#
|
||||||
# output
|
# basic writer
|
||||||
#
|
#
|
||||||
|
|
||||||
public
|
public
|
||||||
|
|
||||||
def write( str )
|
def write( str )
|
||||||
writing {
|
writing {
|
||||||
do_write str
|
do_write str
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def writeline( str )
|
def writeline( str )
|
||||||
writing {
|
writing {
|
||||||
do_write str + "\r\n"
|
do_write str + "\r\n"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_bin( src, block )
|
def write_bin( src, block )
|
||||||
writing {
|
writing {
|
||||||
if block then
|
if block then
|
||||||
block.call WriteAdapter.new(self, :do_write)
|
block.call WriteAdapter.new(self, :do_write)
|
||||||
else
|
else
|
||||||
src.each do |bin|
|
src.each do |bin|
|
||||||
do_write bin
|
do_write bin
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_pendstr( src, block )
|
#
|
||||||
|
# line oriented writer
|
||||||
|
#
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def write_pendstr( src, &block )
|
||||||
D_off "writing text from #{src.type}"
|
D_off "writing text from #{src.type}"
|
||||||
|
|
||||||
wsize = using_each_crlf_line {
|
wsize = using_each_crlf_line {
|
||||||
if block then
|
if block_given? then
|
||||||
block.call WriteAdapter.new(self, :wpend_in)
|
yield WriteAdapter.new(self, :wpend_in)
|
||||||
else
|
else
|
||||||
wpend_in src
|
wpend_in src
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
D_on "wrote #{wsize} bytes text"
|
D_on "wrote #{wsize} bytes text"
|
||||||
@ -688,22 +688,22 @@ module Net
|
|||||||
|
|
||||||
def using_each_crlf_line
|
def using_each_crlf_line
|
||||||
writing {
|
writing {
|
||||||
@wbuf = ''
|
@wbuf = ''
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
if not @wbuf.empty? then # unterminated last line
|
if not @wbuf.empty? then # unterminated last line
|
||||||
if @wbuf[-1] == ?\r then
|
if @wbuf[-1] == ?\r then
|
||||||
@wbuf.chop!
|
@wbuf.chop!
|
||||||
|
end
|
||||||
|
@wbuf.concat "\r\n"
|
||||||
|
do_write @wbuf
|
||||||
|
elsif @writtensize == 0 then # empty src
|
||||||
|
do_write "\r\n"
|
||||||
end
|
end
|
||||||
@wbuf.concat "\r\n"
|
do_write ".\r\n"
|
||||||
do_write @wbuf
|
|
||||||
elsif @writtensize == 0 then # empty src
|
|
||||||
do_write "\r\n"
|
|
||||||
end
|
|
||||||
do_write ".\r\n"
|
|
||||||
|
|
||||||
@wbuf = nil
|
@wbuf = nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -758,34 +758,32 @@ module Net
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# lib (writer)
|
||||||
|
#
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def writing
|
def writing
|
||||||
@writtensize = 0
|
@writtensize = 0
|
||||||
@sending = ''
|
@debugout << '<- ' if @debugout
|
||||||
|
|
||||||
yield
|
yield
|
||||||
|
|
||||||
if @debugout then
|
|
||||||
@debugout << 'write "'
|
|
||||||
@debugout << @sending
|
|
||||||
@debugout << "\"\n"
|
|
||||||
end
|
|
||||||
@socket.flush
|
@socket.flush
|
||||||
|
@debugout << "\n" if @debugout
|
||||||
@writtensize
|
@writtensize
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_write( arg )
|
def do_write( str )
|
||||||
if @debugout or @sending.size < 128 then
|
@debugout << str.dump if @debugout
|
||||||
@sending << Net.quote( arg )
|
@writtensize += (n = @socket.write(str))
|
||||||
else
|
n
|
||||||
@sending << '...' unless @sending[-1] == ?.
|
|
||||||
end
|
|
||||||
|
|
||||||
s = @socket.write( arg )
|
|
||||||
@writtensize += s
|
|
||||||
s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
###
|
||||||
|
### DEBUG
|
||||||
|
###
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
def D_off( msg )
|
def D_off( msg )
|
||||||
D msg
|
D msg
|
||||||
@ -806,14 +804,6 @@ module Net
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def Net.quote( str )
|
|
||||||
str = str.gsub( "\n", '\\n' )
|
|
||||||
str.gsub!( "\r", '\\r' )
|
|
||||||
str.gsub!( "\t", '\\t' )
|
|
||||||
str
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# for backward compatibility
|
# for backward compatibility
|
||||||
module NetPrivate
|
module NetPrivate
|
||||||
Response = ::Net::Response
|
Response = ::Net::Response
|
||||||
|
194
lib/net/smtp.rb
194
lib/net/smtp.rb
@ -92,17 +92,17 @@ like File and Array.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
=== Giving "Hello" Domain
|
=== HELO domain
|
||||||
|
|
||||||
If your machine does not have canonical host name, maybe you
|
In almost all situation, you must designate the third argument
|
||||||
must designate the third argument of SMTP.start.
|
of SMTP.start/SMTP#start. It is the domain name which you are on
|
||||||
|
(the host to send mail from). It is called "HELO domain".
|
||||||
|
SMTP server will judge if he/she should send or reject
|
||||||
|
the SMTP session by inspecting HELO domain.
|
||||||
|
|
||||||
Net::SMTP.start( 'your.smtp.server', 25,
|
Net::SMTP.start( 'your.smtp.server', 25,
|
||||||
'mail.from.domain' ) {|smtp|
|
'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 Net::SMTP
|
||||||
|
|
||||||
@ -111,8 +111,8 @@ send or reject SMTP session by this data.
|
|||||||
: new( address, port = 25 )
|
: new( address, port = 25 )
|
||||||
creates a new Net::SMTP object.
|
creates a new Net::SMTP object.
|
||||||
|
|
||||||
: start( address, port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil )
|
: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil )
|
||||||
: start( address, port = 25, helo_domain = Socket.gethostname, account = nil, password = nil, authtype = nil ) {|smtp| .... }
|
: start( address, port = 25, helo_domain = 'localhost.localdomain', account = nil, password = nil, authtype = nil ) {|smtp| .... }
|
||||||
is equal to
|
is equal to
|
||||||
Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
|
Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
|
||||||
|
|
||||||
@ -179,8 +179,9 @@ send or reject SMTP session by this data.
|
|||||||
|
|
||||||
: ready( from_addr, *to_addrs ) {|adapter| .... }
|
: ready( from_addr, *to_addrs ) {|adapter| .... }
|
||||||
This method stands by the SMTP object for sending mail and
|
This method stands by the SMTP object for sending mail and
|
||||||
give adapter object to the block. ADAPTER accepts only "write"
|
gives adapter object to the block. ADAPTER has these 5 methods:
|
||||||
method.
|
|
||||||
|
puts print printf write <<
|
||||||
|
|
||||||
FROM_ADDR must be a String, representing source mail address.
|
FROM_ADDR must be a String, representing source mail address.
|
||||||
TO_ADDRS must be Strings or an Array of Strings, representing
|
TO_ADDRS must be Strings or an Array of Strings, representing
|
||||||
@ -188,11 +189,13 @@ send or reject SMTP session by this data.
|
|||||||
|
|
||||||
# example
|
# example
|
||||||
Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
|
Net::SMTP.start( 'your.smtp.server', 25 ) {|smtp|
|
||||||
smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) do |adapter|
|
smtp.ready( 'from@mail.addr', 'dest@mail.addr' ) {|f|
|
||||||
adapter.write str1
|
f.puts 'From: aamine@loveruby.net'
|
||||||
adapter.write str2
|
f.puts 'To: someone@somedomain.org'
|
||||||
adapter.write str3
|
f.puts 'Subject: test mail'
|
||||||
end
|
f.puts
|
||||||
|
f.puts 'This is test mail.'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
== Exceptions
|
== Exceptions
|
||||||
@ -215,13 +218,11 @@ require 'digest/md5'
|
|||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
|
|
||||||
class SMTP < Protocol
|
class SMTP < Protocol
|
||||||
|
|
||||||
protocol_param :port, '25'
|
protocol_param :port, '25'
|
||||||
protocol_param :command_type, '::Net::SMTPCommand'
|
protocol_param :command_type, '::Net::SMTPCommand'
|
||||||
|
|
||||||
|
|
||||||
def initialize( addr, port = nil )
|
def initialize( addr, port = nil )
|
||||||
super
|
super
|
||||||
@esmtp = true
|
@esmtp = true
|
||||||
@ -229,49 +230,23 @@ module Net
|
|||||||
|
|
||||||
attr :esmtp
|
attr :esmtp
|
||||||
|
|
||||||
def send_mail( mailsrc, from_addr, *to_addrs )
|
|
||||||
do_ready from_addr, to_addrs.flatten
|
|
||||||
@command.write_mail mailsrc, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
alias sendmail send_mail
|
|
||||||
|
|
||||||
def ready( from_addr, *to_addrs, &block )
|
|
||||||
do_ready from_addr, to_addrs.flatten
|
|
||||||
@command.write_mail nil, block
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def do_start( helo = 'localhost.localdomain',
|
||||||
def do_ready( from_addr, to_addrs )
|
|
||||||
if to_addrs.empty? then
|
|
||||||
raise ArgumentError, 'mail destination does not given'
|
|
||||||
end
|
|
||||||
@command.mailfrom from_addr
|
|
||||||
@command.rcpt to_addrs
|
|
||||||
@command.data
|
|
||||||
end
|
|
||||||
|
|
||||||
def do_start( helodom = nil,
|
|
||||||
user = nil, secret = nil, authtype = nil )
|
user = nil, secret = nil, authtype = nil )
|
||||||
helodom ||= ::Socket.gethostname
|
conn_socket
|
||||||
unless helodom then
|
conn_command
|
||||||
raise ArgumentError,
|
|
||||||
"cannot get localhost name; try 'smtp.start(local_host_name)'"
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if @esmtp then
|
if @esmtp then
|
||||||
@command.ehlo helodom
|
command().ehlo helo
|
||||||
else
|
else
|
||||||
@command.helo helodom
|
command().helo helo
|
||||||
end
|
end
|
||||||
rescue ProtocolError
|
rescue ProtocolError
|
||||||
if @esmtp then
|
if @esmtp then
|
||||||
@esmtp = false
|
@esmtp = false
|
||||||
@command.error_ok
|
command().error_ok
|
||||||
retry
|
retry
|
||||||
else
|
else
|
||||||
raise
|
raise
|
||||||
@ -283,110 +258,133 @@ module Net
|
|||||||
raise ArgumentError, 'both of account and password are required'
|
raise ArgumentError, 'both of account and password are required'
|
||||||
|
|
||||||
mid = 'auth_' + (authtype || 'cram_md5').to_s
|
mid = 'auth_' + (authtype || 'cram_md5').to_s
|
||||||
@command.respond_to? mid or
|
command().respond_to? mid or
|
||||||
raise ArgumentError, "wrong auth type #{authtype.to_s}"
|
raise ArgumentError, "wrong auth type #{authtype.to_s}"
|
||||||
|
|
||||||
@command.__send__ mid, user, secret
|
command().__send__ mid, user, secret
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def do_finish
|
||||||
|
disconn_command
|
||||||
|
disconn_socket
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# SMTP operations
|
||||||
|
#
|
||||||
|
|
||||||
|
public
|
||||||
|
|
||||||
|
def send_mail( mailsrc, from_addr, *to_addrs )
|
||||||
|
do_ready from_addr, to_addrs.flatten
|
||||||
|
command().write_mail mailsrc, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
alias sendmail send_mail
|
||||||
|
|
||||||
|
def ready( from_addr, *to_addrs, &block )
|
||||||
|
do_ready from_addr, to_addrs.flatten
|
||||||
|
command().write_mail nil, block
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def do_ready( from_addr, to_addrs )
|
||||||
|
if to_addrs.empty? then
|
||||||
|
raise ArgumentError, 'mail destination does not given'
|
||||||
|
end
|
||||||
|
command().mailfrom from_addr
|
||||||
|
command().rcpt to_addrs
|
||||||
|
command().data
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
SMTPSession = SMTP
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SMTPCommand < Command
|
class SMTPCommand < Command
|
||||||
|
|
||||||
def initialize( sock )
|
def initialize( sock )
|
||||||
super
|
super
|
||||||
critical {
|
atomic {
|
||||||
check_reply SuccessCode
|
check_reply SuccessCode
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def helo( domain )
|
||||||
def helo( fromdom )
|
atomic {
|
||||||
critical {
|
getok sprintf('HELO %s', domain)
|
||||||
getok sprintf( 'HELO %s', fromdom )
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ehlo( domain )
|
||||||
def ehlo( fromdom )
|
atomic {
|
||||||
critical {
|
getok sprintf('EHLO %s', domain)
|
||||||
getok sprintf( 'EHLO %s', fromdom )
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# "PLAIN" authentication [RFC2554]
|
# "PLAIN" authentication [RFC2554]
|
||||||
def auth_plain( user, secret )
|
def auth_plain( user, secret )
|
||||||
critical {
|
atomic {
|
||||||
getok sprintf( 'AUTH PLAIN %s',
|
getok sprintf('AUTH PLAIN %s',
|
||||||
["\0#{user}\0#{secret}"].pack('m').chomp )
|
["\0#{user}\0#{secret}"].pack('m').chomp)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# "CRAM-MD5" authentication [RFC2195]
|
# "CRAM-MD5" authentication [RFC2195]
|
||||||
def auth_cram_md5( user, secret )
|
def auth_cram_md5( user, secret )
|
||||||
critical {
|
atomic {
|
||||||
rep = getok( 'AUTH CRAM-MD5', ContinueCode )
|
rep = getok( 'AUTH CRAM-MD5', ContinueCode )
|
||||||
challenge = rep.msg.split(' ')[1].unpack('m')[0]
|
challenge = rep.msg.split(' ')[1].unpack('m')[0]
|
||||||
secret = Digest::MD5.digest( secret ) if secret.size > 64
|
secret = Digest::MD5.digest(secret) if secret.size > 64
|
||||||
|
|
||||||
isecret = secret + "\0" * (64 - secret.size)
|
isecret = secret + "\0" * (64 - secret.size)
|
||||||
osecret = isecret.dup
|
osecret = isecret.dup
|
||||||
0.upto( 63 ) do |i|
|
0.upto( 63 ) do |i|
|
||||||
isecret[i] ^= 0x36
|
isecret[i] ^= 0x36
|
||||||
osecret[i] ^= 0x5c
|
osecret[i] ^= 0x5c
|
||||||
end
|
end
|
||||||
tmp = Digest::MD5.digest( isecret + challenge )
|
tmp = Digest::MD5.digest( isecret + challenge )
|
||||||
tmp = Digest::MD5.hexdigest( osecret + tmp )
|
tmp = Digest::MD5.hexdigest( osecret + tmp )
|
||||||
|
|
||||||
getok [user + ' ' + tmp].pack('m').chomp
|
getok [user + ' ' + tmp].pack('m').chomp
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def mailfrom( fromaddr )
|
def mailfrom( fromaddr )
|
||||||
critical {
|
atomic {
|
||||||
getok sprintf( 'MAIL FROM:<%s>', fromaddr )
|
getok sprintf('MAIL FROM:<%s>', fromaddr)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def rcpt( toaddrs )
|
def rcpt( toaddrs )
|
||||||
toaddrs.each do |i|
|
toaddrs.each do |i|
|
||||||
critical {
|
atomic {
|
||||||
getok sprintf( 'RCPT TO:<%s>', i )
|
getok sprintf('RCPT TO:<%s>', i)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def data
|
def data
|
||||||
return unless begin_critical
|
return unless begin_atomic
|
||||||
getok 'DATA', ContinueCode
|
getok 'DATA', ContinueCode
|
||||||
end
|
end
|
||||||
|
|
||||||
def write_mail( mailsrc, block )
|
def write_mail( mailsrc, block )
|
||||||
@socket.write_pendstr mailsrc, block
|
@socket.write_pendstr mailsrc, &block
|
||||||
check_reply SuccessCode
|
check_reply SuccessCode
|
||||||
end_critical
|
end_atomic
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def quit
|
def quit
|
||||||
critical {
|
atomic {
|
||||||
getok 'QUIT'
|
getok 'QUIT'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
|
||||||
def get_reply
|
def get_reply
|
||||||
arr = read_reply
|
arr = read_reply
|
||||||
stat = arr[0][0,3]
|
stat = arr[0][0,3]
|
||||||
@ -407,7 +405,6 @@ module Net
|
|||||||
Response.new( klass, stat, arr.join('') )
|
Response.new( klass, stat, arr.join('') )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def read_reply
|
def read_reply
|
||||||
arr = []
|
arr = []
|
||||||
while true do
|
while true do
|
||||||
@ -424,6 +421,9 @@ module Net
|
|||||||
|
|
||||||
|
|
||||||
# for backward compatibility
|
# for backward compatibility
|
||||||
|
|
||||||
|
SMTPSession = SMTP
|
||||||
|
|
||||||
module NetPrivate
|
module NetPrivate
|
||||||
SMTPCommand = ::Net::SMTPCommand
|
SMTPCommand = ::Net::SMTPCommand
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user