~~~
This commit is contained in:
parent
3de478f8ba
commit
6c39ea859b
4 changed files with 82 additions and 30 deletions
|
@ -5,11 +5,10 @@ describe Imap do
|
||||||
|
|
||||||
it "should count emails in mailbox" do
|
it "should count emails in mailbox" do
|
||||||
imap = Imap::Client.new(host: "imap.gmail.com", port: 993, username: "***", password: "***")
|
imap = Imap::Client.new(host: "imap.gmail.com", port: 993, username: "***", password: "***")
|
||||||
mailboxes = imap.get_mailboxes
|
mailboxes = imap.list
|
||||||
if mailboxes.size > 0
|
if mailboxes.size > 0
|
||||||
mailbox = mailboxes[0]
|
mailbox = mailboxes.first
|
||||||
imap.set_mailbox(mailbox)
|
message_count = imap.status(mailbox, ["MESSAGES"])["MESSAGES"]
|
||||||
message_count = imap.get_message_count
|
|
||||||
puts "There are #{message_count} message in #{mailbox}"
|
puts "There are #{message_count} message in #{mailbox}"
|
||||||
end
|
end
|
||||||
imap.close
|
imap.close
|
||||||
|
|
51
src/imap.cr
51
src/imap.cr
|
@ -1,12 +1,16 @@
|
||||||
require "./imap/*"
|
|
||||||
require "openssl"
|
require "openssl"
|
||||||
require "logger"
|
require "logger"
|
||||||
|
|
||||||
|
require "./imap/*"
|
||||||
|
|
||||||
module Imap
|
module Imap
|
||||||
class Client
|
class Client
|
||||||
|
CAPS_UTF8 = ImmutableSet.new("UTF8=ALL", "UTF8=ONLY", "UTF8=ACCEPT")
|
||||||
|
|
||||||
@caps : Set(String)
|
@caps : Set(String)
|
||||||
@socket : TCPSocket | OpenSSL::SSL::Socket::Client | Nil = nil
|
@socket : TCPSocket | OpenSSL::SSL::Socket::Client | Nil = nil
|
||||||
@logger : Logger
|
@logger : Logger
|
||||||
|
@idling = false
|
||||||
|
|
||||||
def initialize(host = "imap.gmail.com", port = 993, username = "", password = "", loglevel = Logger::ERROR)
|
def initialize(host = "imap.gmail.com", port = 993, username = "", password = "", loglevel = Logger::ERROR)
|
||||||
@logger = Logger.new(STDERR)
|
@logger = Logger.new(STDERR)
|
||||||
|
@ -16,11 +20,13 @@ module Imap
|
||||||
tls_socket = OpenSSL::SSL::Socket::Client.new(@socket.as(TCPSocket), sync_close: true, hostname: host)
|
tls_socket = OpenSSL::SSL::Socket::Client.new(@socket.as(TCPSocket), sync_close: true, hostname: host)
|
||||||
tls_socket.sync = false
|
tls_socket.sync = false
|
||||||
@socket = tls_socket
|
@socket = tls_socket
|
||||||
|
|
||||||
login(username, password)
|
login(username, password)
|
||||||
# list headers
|
|
||||||
# process_mail_headers(command("tag FETCH 1:#{count} (BODY[HEADER])"))
|
|
||||||
|
|
||||||
@caps = capability
|
@caps = capability
|
||||||
|
if CAPS_UTF8.intersects? @caps
|
||||||
|
command("ENABLE", "UTF8=ACCEPT")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def socket
|
private def socket
|
||||||
|
@ -31,22 +37,20 @@ module Imap
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private def command(command : String, *parameters, &block : String -> Bool)
|
private def send(data : String)
|
||||||
command_and_parameter = case command
|
socket << data << "\r\n"
|
||||||
when "DONE"
|
socket.flush
|
||||||
command
|
@logger.debug "Sent: #{data}"
|
||||||
else
|
end
|
||||||
"tag #{command}"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
private def command(command : String, *parameters, &block : String -> Bool)
|
||||||
|
command_and_parameter = "tag #{command}"
|
||||||
if parameters.size > 0
|
if parameters.size > 0
|
||||||
params = parameters.join(" ")
|
params = parameters.join(" ")
|
||||||
command_and_parameter += " #{params}"
|
command_and_parameter += " #{params}"
|
||||||
end
|
end
|
||||||
|
|
||||||
socket << command_and_parameter << "\r\n"
|
send command_and_parameter
|
||||||
socket.flush
|
|
||||||
@logger.debug "Sent: #{command_and_parameter}"
|
|
||||||
|
|
||||||
while (line = socket.gets)
|
while (line = socket.gets)
|
||||||
@logger.debug " Got: #{line}"
|
@logger.debug " Got: #{line}"
|
||||||
|
@ -64,7 +68,7 @@ module Imap
|
||||||
res << line
|
res << line
|
||||||
next false
|
next false
|
||||||
elsif line =~ /^tag (BAD|NO)/
|
elsif line =~ /^tag (BAD|NO)/
|
||||||
raise "Invalid responce \"#{line}\" received."
|
raise "Invalid response \"#{line}\" received."
|
||||||
else
|
else
|
||||||
res << line
|
res << line
|
||||||
end
|
end
|
||||||
|
@ -75,7 +79,7 @@ module Imap
|
||||||
end
|
end
|
||||||
|
|
||||||
private def login(username, password)
|
private def login(username, password)
|
||||||
command("login", username, password)
|
command("LOGIN", username, password)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends a CAPABILITY command
|
# Sends a CAPABILITY command
|
||||||
|
@ -92,20 +96,24 @@ module Imap
|
||||||
spawn do
|
spawn do
|
||||||
command("IDLE") do |line|
|
command("IDLE") do |line|
|
||||||
if line =~ /\+ idling/
|
if line =~ /\+ idling/
|
||||||
# nothing to do
|
@idling = true
|
||||||
elsif line =~ /\* (\d+) ([A-Z]+)/
|
elsif line =~ /\* (\d+) ([A-Z]+)/
|
||||||
block.call($2, $1.to_u32)
|
block.call($2, $1.to_u32)
|
||||||
else
|
else
|
||||||
|
@idling = false
|
||||||
next false
|
next false
|
||||||
end
|
end
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Fiber.yield
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends a DONE command
|
# Sends a DONE command
|
||||||
def done
|
def idle_done
|
||||||
command("DONE")
|
raise "IDLE not started" unless @idling
|
||||||
|
send("DONE")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends a SELECT command to select a +mailbox+ so that messages
|
# Sends a SELECT command to select a +mailbox+ so that messages
|
||||||
|
@ -134,7 +142,7 @@ module Imap
|
||||||
|
|
||||||
|
|
||||||
# Returns an array of mailbox names
|
# Returns an array of mailbox names
|
||||||
def list : Set(String)
|
def list : Array(String)
|
||||||
mailboxes = [] of String
|
mailboxes = [] of String
|
||||||
res = command(%{LIST "" "*"})
|
res = command(%{LIST "" "*"})
|
||||||
res.each do |line|
|
res.each do |line|
|
||||||
|
@ -143,8 +151,7 @@ module Imap
|
||||||
mailboxes << name[1].to_s if name
|
mailboxes << name[1].to_s if name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# TODO: decode MIME encoded UTF-8 strings
|
return mailboxes
|
||||||
return Set.new(mailboxes)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sends a STATUS command, and returns the status of the indicated
|
# Sends a STATUS command, and returns the status of the indicated
|
||||||
|
@ -198,7 +205,7 @@ module Imap
|
||||||
# Closes the imap connection
|
# Closes the imap connection
|
||||||
def close
|
def close
|
||||||
command("LOGOUT") rescue nil
|
command("LOGOUT") rescue nil
|
||||||
@socket.close
|
@socket.not_nil!.close if @socket
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
45
src/imap/immutable_set.cr
Normal file
45
src/imap/immutable_set.cr
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
class ImmutableSet(T)
|
||||||
|
@set : Set(T)
|
||||||
|
|
||||||
|
def initialize(*params : T)
|
||||||
|
@set = Set.new(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def each
|
||||||
|
@set.each
|
||||||
|
end
|
||||||
|
|
||||||
|
def size
|
||||||
|
@set.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s(io)
|
||||||
|
io << "ImmutableSet{"
|
||||||
|
join ", ", io, &.inspect(io)
|
||||||
|
io << '}'
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
@set.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect(io)
|
||||||
|
to_s(io)
|
||||||
|
end
|
||||||
|
|
||||||
|
def object_id
|
||||||
|
@set.object_id
|
||||||
|
end
|
||||||
|
|
||||||
|
def includes?(obj)
|
||||||
|
@set.includes?(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
def intersects?(other)
|
||||||
|
@set.intersects?(other)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print(pp) : Nil
|
||||||
|
pp.list("ImmutableSet{", @set, "}")
|
||||||
|
end
|
||||||
|
end
|
9
test.cr
9
test.cr
|
@ -10,13 +10,14 @@ end
|
||||||
imap = Imap::Client.new(host: "imap.gmail.com", port: 993, username: ARGV[0], password: ARGV[1], loglevel: Logger::DEBUG)
|
imap = Imap::Client.new(host: "imap.gmail.com", port: 993, username: ARGV[0], password: ARGV[1], loglevel: Logger::DEBUG)
|
||||||
mailboxes = imap.list
|
mailboxes = imap.list
|
||||||
if mailboxes.includes?("INBOX")
|
if mailboxes.includes?("INBOX")
|
||||||
|
pp imap.status("INBOX", ["MESSAGES", "RECENT"])
|
||||||
|
|
||||||
imap.select("INBOX")
|
imap.select("INBOX")
|
||||||
imap.idle do |name, value|
|
imap.idle do |name, value|
|
||||||
puts "#{name} => #{value}"
|
puts "#{name} => #{value}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
while true
|
sleep 3
|
||||||
sleep 5
|
imap.idle_done
|
||||||
imap.done
|
imap.close
|
||||||
end
|
|
||||||
|
|
Loading…
Reference in a new issue