mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* lib/net/imap.rb: supports response handlers and multiple commands.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1283 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
4beca99d16
commit
2d32e4dd6d
2 changed files with 168 additions and 80 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
Mon Mar 26 21:16:56 2001 Shugo Maeda <shugo@ruby-lang.org>
|
||||||
|
|
||||||
|
* lib/net/imap.rb: supports response handlers and multiple commands.
|
||||||
|
|
||||||
Mon Mar 26 17:21:07 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
|
Mon Mar 26 17:21:07 2001 Yukihiro Matsumoto <matz@ruby-lang.org>
|
||||||
|
|
||||||
* eval.c: remove TMP_PROTECT_END to prevent C_ALLOCA crash.
|
* eval.c: remove TMP_PROTECT_END to prevent C_ALLOCA crash.
|
||||||
|
|
244
lib/net/imap.rb
244
lib/net/imap.rb
|
@ -11,6 +11,20 @@ You can freely distribute/modify this library.
|
||||||
|
|
||||||
Net::IMAP implements Internet Message Access Protocol (IMAP) clients.
|
Net::IMAP implements Internet Message Access Protocol (IMAP) clients.
|
||||||
|
|
||||||
|
Net::IMAP supports multiple commands. For example,
|
||||||
|
|
||||||
|
imap = Net::IMAP.new("imap.foo.net", "imap2")
|
||||||
|
imap.authenticate("cram-md5", "bar", "password")
|
||||||
|
imap.select("inbox")
|
||||||
|
t = Thread.start {
|
||||||
|
p imap.fetch(1..-1, "UID")
|
||||||
|
}
|
||||||
|
p imap.search(["BODY", "hello"])
|
||||||
|
t.join
|
||||||
|
imap.disconnect
|
||||||
|
|
||||||
|
This script invokes the FETCH command and the SEARCH command concurrently.
|
||||||
|
|
||||||
=== Super Class
|
=== Super Class
|
||||||
|
|
||||||
Object
|
Object
|
||||||
|
@ -210,14 +224,26 @@ Object
|
||||||
p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
|
p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII")
|
||||||
#=> [6, 7, 8, 1]
|
#=> [6, 7, 8, 1]
|
||||||
|
|
||||||
|
: add_response_handler(handler = Proc.new)
|
||||||
|
Adds a response handler.
|
||||||
|
|
||||||
|
: remove_response_handler(handler)
|
||||||
|
Removes the response handler.
|
||||||
|
|
||||||
|
: response_handlers
|
||||||
|
Returns all response handlers.
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
require "socket"
|
require "socket"
|
||||||
|
require "monitor"
|
||||||
require "md5"
|
require "md5"
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
class IMAP
|
class IMAP
|
||||||
attr_reader :greeting, :responses
|
include MonitorMixin
|
||||||
|
|
||||||
|
attr_reader :greeting, :responses, :response_handlers
|
||||||
|
|
||||||
def self.debug
|
def self.debug
|
||||||
return @@debug
|
return @@debug
|
||||||
|
@ -232,12 +258,16 @@ module Net
|
||||||
end
|
end
|
||||||
|
|
||||||
def disconnect
|
def disconnect
|
||||||
|
@sock.shutdown
|
||||||
|
@receiver_thread.join
|
||||||
@sock.close
|
@sock.close
|
||||||
end
|
end
|
||||||
|
|
||||||
def capability
|
def capability
|
||||||
send_command("CAPABILITY")
|
synchronize do
|
||||||
return @responses.delete("CAPABILITY")[-1]
|
send_command("CAPABILITY")
|
||||||
|
return @responses.delete("CAPABILITY")[-1]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def noop
|
def noop
|
||||||
|
@ -268,13 +298,17 @@ module Net
|
||||||
end
|
end
|
||||||
|
|
||||||
def select(mailbox)
|
def select(mailbox)
|
||||||
@responses.clear
|
synchronize do
|
||||||
send_command("SELECT", mailbox)
|
@responses.clear
|
||||||
|
send_command("SELECT", mailbox)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def examine(mailbox)
|
def examine(mailbox)
|
||||||
@responses.clear
|
synchronize do
|
||||||
send_command("EXAMINE", mailbox)
|
@responses.clear
|
||||||
|
send_command("EXAMINE", mailbox)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(mailbox)
|
def create(mailbox)
|
||||||
|
@ -298,18 +332,24 @@ module Net
|
||||||
end
|
end
|
||||||
|
|
||||||
def list(refname, mailbox)
|
def list(refname, mailbox)
|
||||||
send_command("LIST", refname, mailbox)
|
synchronize do
|
||||||
return @responses.delete("LIST")
|
send_command("LIST", refname, mailbox)
|
||||||
|
return @responses.delete("LIST")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def lsub(refname, mailbox)
|
def lsub(refname, mailbox)
|
||||||
send_command("LSUB", refname, mailbox)
|
synchronize do
|
||||||
return @responses.delete("LSUB")
|
send_command("LSUB", refname, mailbox)
|
||||||
|
return @responses.delete("LSUB")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def status(mailbox, attr)
|
def status(mailbox, attr)
|
||||||
send_command("STATUS", mailbox, attr)
|
synchronize do
|
||||||
return @responses.delete("STATUS")[-1][1]
|
send_command("STATUS", mailbox, attr)
|
||||||
|
return @responses.delete("STATUS")[-1][1]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def append(mailbox, message, flags = nil, date_time = nil)
|
def append(mailbox, message, flags = nil, date_time = nil)
|
||||||
|
@ -331,8 +371,10 @@ module Net
|
||||||
end
|
end
|
||||||
|
|
||||||
def expunge
|
def expunge
|
||||||
send_command("EXPUNGE")
|
synchronize do
|
||||||
return @responses.delete("EXPUNGE")
|
send_command("EXPUNGE")
|
||||||
|
return @responses.delete("EXPUNGE")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def search(keys, charset = nil)
|
def search(keys, charset = nil)
|
||||||
|
@ -375,6 +417,14 @@ module Net
|
||||||
return sort_internal("UID SORT", sort_keys, search_keys, charset)
|
return sort_internal("UID SORT", sort_keys, search_keys, charset)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_response_handler(handler = Proc.new)
|
||||||
|
@response_handlers.push(handler)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_response_handler(handler)
|
||||||
|
@response_handlers.delete(handler)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
CRLF = "\r\n"
|
CRLF = "\r\n"
|
||||||
|
@ -384,6 +434,7 @@ module Net
|
||||||
@@authenticators = {}
|
@@authenticators = {}
|
||||||
|
|
||||||
def initialize(host, port = PORT)
|
def initialize(host, port = PORT)
|
||||||
|
super()
|
||||||
@host = host
|
@host = host
|
||||||
@port = port
|
@port = port
|
||||||
@tag_prefix = "RUBY"
|
@tag_prefix = "RUBY"
|
||||||
|
@ -391,69 +442,54 @@ module Net
|
||||||
@parser = ResponseParser.new
|
@parser = ResponseParser.new
|
||||||
@sock = TCPSocket.open(host, port)
|
@sock = TCPSocket.open(host, port)
|
||||||
@responses = Hash.new([].freeze)
|
@responses = Hash.new([].freeze)
|
||||||
|
@tagged_responses = {}
|
||||||
|
@response_handlers = []
|
||||||
|
@tag_arrival = new_cond
|
||||||
|
|
||||||
@greeting = get_response
|
@greeting = get_response
|
||||||
if /\ABYE\z/ni =~ @greeting.name
|
if /\ABYE\z/ni =~ @greeting.name
|
||||||
@sock.close
|
@sock.close
|
||||||
raise ByeResponseError, resp[0]
|
raise ByeResponseError, resp[0]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@receiver_thread = Thread.start {
|
||||||
|
receive_responses
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_command(cmd, *args, &block)
|
def receive_responses
|
||||||
tag = generate_tag
|
|
||||||
data = args.collect {|i| format_data(i)}.join(" ")
|
|
||||||
if data.length > 0
|
|
||||||
put_line(tag + " " + cmd + " " + data)
|
|
||||||
else
|
|
||||||
put_line(tag + " " + cmd)
|
|
||||||
end
|
|
||||||
return get_all_responses(tag, cmd, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_tag
|
|
||||||
@tagno += 1
|
|
||||||
return format("%s%04d", @tag_prefix, @tagno)
|
|
||||||
end
|
|
||||||
|
|
||||||
def send_data(*args)
|
|
||||||
data = args.collect {|i| format_data(i)}.join(" ")
|
|
||||||
put_line(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
def put_line(line)
|
|
||||||
line = line + CRLF
|
|
||||||
@sock.print(line)
|
|
||||||
if @@debug
|
|
||||||
$stderr.print(line.gsub(/^/n, "C: "))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_all_responses(tag, cmd, &block)
|
|
||||||
while resp = get_response
|
while resp = get_response
|
||||||
if @@debug
|
synchronize do
|
||||||
$stderr.printf("R: %s\n", resp.inspect)
|
case resp
|
||||||
end
|
when TaggedResponse
|
||||||
case resp
|
@tagged_responses[resp.tag] = resp
|
||||||
when TaggedResponse
|
@tag_arrival.broadcast
|
||||||
case resp.name
|
when UntaggedResponse
|
||||||
when /\A(?:NO)\z/ni
|
record_response(resp.name, resp.data)
|
||||||
raise NoResponseError, resp.data.text
|
if resp.data.instance_of?(ResponseText) &&
|
||||||
when /\A(?:BAD)\z/ni
|
(code = resp.data.code)
|
||||||
raise BadResponseError, resp.data.text
|
record_response(code.name, code.data)
|
||||||
else
|
end
|
||||||
return resp
|
|
||||||
end
|
end
|
||||||
when UntaggedResponse
|
@response_handlers.each do |handler|
|
||||||
if /\ABYE\z/ni =~ resp.name &&
|
handler.call(resp)
|
||||||
cmd != "LOGOUT"
|
|
||||||
raise ByeResponseError, resp.data.text
|
|
||||||
end
|
|
||||||
record_response(resp.name, resp.data)
|
|
||||||
if resp.data.instance_of?(ResponseText) &&
|
|
||||||
(code = resp.data.code)
|
|
||||||
record_response(code.name, code.data)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
block.call(resp) if block
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_tagged_response(tag, cmd)
|
||||||
|
until @tagged_responses.key?(tag)
|
||||||
|
@tag_arrival.wait
|
||||||
|
end
|
||||||
|
resp = @tagged_responses.delete(tag)
|
||||||
|
case resp.name
|
||||||
|
when /\A(?:NO)\z/ni
|
||||||
|
raise NoResponseError, resp.data.text
|
||||||
|
when /\A(?:BAD)\z/ni
|
||||||
|
raise BadResponseError, resp.data.text
|
||||||
|
else
|
||||||
|
return resp
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -484,6 +520,46 @@ module Net
|
||||||
@responses[name].push(data)
|
@responses[name].push(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def send_command(cmd, *args, &block)
|
||||||
|
synchronize do
|
||||||
|
tag = generate_tag
|
||||||
|
data = args.collect {|i| format_data(i)}.join(" ")
|
||||||
|
if data.length > 0
|
||||||
|
put_line(tag + " " + cmd + " " + data)
|
||||||
|
else
|
||||||
|
put_line(tag + " " + cmd)
|
||||||
|
end
|
||||||
|
if block
|
||||||
|
add_response_handler(block)
|
||||||
|
end
|
||||||
|
begin
|
||||||
|
return get_tagged_response(tag, cmd)
|
||||||
|
ensure
|
||||||
|
if block
|
||||||
|
remove_response_handler(block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_tag
|
||||||
|
@tagno += 1
|
||||||
|
return format("%s%04d", @tag_prefix, @tagno)
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_data(*args)
|
||||||
|
data = args.collect {|i| format_data(i)}.join(" ")
|
||||||
|
put_line(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_line(line)
|
||||||
|
line = line + CRLF
|
||||||
|
@sock.print(line)
|
||||||
|
if @@debug
|
||||||
|
$stderr.print(line.gsub(/^/n, "C: "))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def format_data(data)
|
def format_data(data)
|
||||||
case data
|
case data
|
||||||
when nil
|
when nil
|
||||||
|
@ -550,30 +626,36 @@ module Net
|
||||||
else
|
else
|
||||||
normalize_searching_criteria(keys)
|
normalize_searching_criteria(keys)
|
||||||
end
|
end
|
||||||
if charset
|
synchronize do
|
||||||
send_command(cmd, "CHARSET", charset, *keys)
|
if charset
|
||||||
else
|
send_command(cmd, "CHARSET", charset, *keys)
|
||||||
send_command(cmd, *keys)
|
else
|
||||||
|
send_command(cmd, *keys)
|
||||||
|
end
|
||||||
|
return @responses.delete("SEARCH")[-1]
|
||||||
end
|
end
|
||||||
return @responses.delete("SEARCH")[-1]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_internal(cmd, set, attr)
|
def fetch_internal(cmd, set, attr)
|
||||||
if attr.instance_of?(String)
|
if attr.instance_of?(String)
|
||||||
attr = RawData.new(attr)
|
attr = RawData.new(attr)
|
||||||
end
|
end
|
||||||
@responses.delete("FETCH")
|
synchronize do
|
||||||
send_command(cmd, MessageSet.new(set), attr)
|
@responses.delete("FETCH")
|
||||||
return @responses.delete("FETCH")
|
send_command(cmd, MessageSet.new(set), attr)
|
||||||
|
return @responses.delete("FETCH")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def store_internal(cmd, set, attr, flags)
|
def store_internal(cmd, set, attr, flags)
|
||||||
if attr.instance_of?(String)
|
if attr.instance_of?(String)
|
||||||
attr = RawData.new(attr)
|
attr = RawData.new(attr)
|
||||||
end
|
end
|
||||||
@responses.delete("FETCH")
|
synchronize do
|
||||||
send_command(cmd, MessageSet.new(set), attr, flags)
|
@responses.delete("FETCH")
|
||||||
return @responses.delete("FETCH")
|
send_command(cmd, MessageSet.new(set), attr, flags)
|
||||||
|
return @responses.delete("FETCH")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def copy_internal(cmd, set, mailbox)
|
def copy_internal(cmd, set, mailbox)
|
||||||
|
@ -587,8 +669,10 @@ module Net
|
||||||
normalize_searching_criteria(search_keys)
|
normalize_searching_criteria(search_keys)
|
||||||
end
|
end
|
||||||
normalize_searching_criteria(search_keys)
|
normalize_searching_criteria(search_keys)
|
||||||
send_command(cmd, sort_keys, charset, *search_keys)
|
synchronize do
|
||||||
return @responses.delete("SORT")[-1]
|
send_command(cmd, sort_keys, charset, *search_keys)
|
||||||
|
return @responses.delete("SORT")[-1]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize_searching_criteria(keys)
|
def normalize_searching_criteria(keys)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue