Class: EventMachine::IMAP::Client
- Inherits:
-
Object
- Object
- EventMachine::IMAP::Client
- Includes:
- EM::Deferrable, Authenticators
- Defined in:
- lib/em-imap/client.rb
Overview
TODO: Anything that accepts or returns a mailbox name should have UTF7 support.
Instance Method Summary collapse
- #add_response_handler(&block) ⇒ Object
-
#append(mailbox, message, flags = nil, date_time = nil) ⇒ Object
Add a message to the mailbox.
-
#authenticate(auth_type, *args) ⇒ Object
Authenticate using a custom authenticator.
-
#capability ⇒ Object
Ask the server which capabilities it supports.
-
#check ⇒ Object
Checkpoint the current mailbox.
-
#close ⇒ Object
Unselect the current mailbox.
- #connect ⇒ Object
-
#copy(seq, mailbox) ⇒ Object
Copy the specified messages to another mailbox.
-
#create(mailbox) ⇒ Object
Create a new mailbox with the given name.
-
#delete(mailbox) ⇒ Object
Delete the mailbox with this name.
- #disconnect ⇒ Object
-
#examine(mailbox) ⇒ Object
Select a mailbox for performing read-only commands.
-
#expunge ⇒ Object
Permanently remove any messages with the Deleted flag from the current mailbox.
-
#fetch(seq, attr = "FULL") ⇒ Object
Get the contents of, or information about, a message.
-
#idle(&block) ⇒ Object
The IDLE command allows you to wait for any untagged responses that give status updates about the contents of a mailbox.
-
#initialize(host, port, usessl = false) ⇒ Client
constructor
A new instance of Client.
-
#list(refname = "", pattern = "*") ⇒ Object
List all available mailboxes.
-
#login(username, password) ⇒ Object
Authenticate with a username and password.
-
#logout ⇒ Object
Logout and close the connection.
-
#lsub(refname, pattern) ⇒ Object
List all subscribed mailboxes.
-
#noop ⇒ Object
Actively do nothing.
-
#rename(oldname, newname) ⇒ Object
Rename the mailbox with this name.
-
#search(*args) ⇒ Object
Search for messages in the current mailbox.
-
#select(mailbox) ⇒ Object
Select a mailbox for performing commands against.
-
#sort(sort_keys, *args) ⇒ Object
SORT and THREAD (like SEARCH) from tools.ietf.org/search/rfc5256.
-
#starttls ⇒ Object
(also: #start_tls)
Run a STARTTLS handshake.
-
#status(mailbox, attrs = ['MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN']) ⇒ Object
Get the status of a mailbox.
-
#store(seq, name, value) ⇒ Object
Update the flags on a message.
-
#subscribe(mailbox) ⇒ Object
Add this mailbox to the list of subscribed mailboxes.
- #thread(algorithm, *args) ⇒ Object
-
#uid_copy(seq, mailbox) ⇒ Object
The same as copy, but keyed off UIDs instead of sequence numbers.
-
#uid_fetch(seq, attr = "FULL") ⇒ Object
The same as fetch, but keyed of UIDs instead of sequence numbers.
-
#uid_search(*args) ⇒ Object
The same as search, but succeeding with a list of UIDs not sequence numbers.
- #uid_sort(sort_keys, *args) ⇒ Object
-
#uid_store(seq, name, value) ⇒ Object
The same as store, but keyed off UIDs instead of sequence numbers.
- #uid_thread(algorithm, *args) ⇒ Object
-
#unsubscribe(mailbox) ⇒ Object
Remove this mailbox from the list of subscribed mailboxes.
-
#wait_for_new_emails(wrapper = Listener.new, &block) ⇒ Object
Wait for new emails to arrive, and call the block when they do.
-
#wait_for_one_email(timeout = 29 * 60) ⇒ Object
A Wrapper around the IDLE command that lets you wait until one email is received.
Methods included from Authenticators
Constructor Details
#initialize(host, port, usessl = false) ⇒ Client
Returns a new instance of Client.
10 11 12 |
# File 'lib/em-imap/client.rb', line 10 def initialize(host, port, usessl=false) @connect_args=[host, port, usessl] end |
Instance Method Details
#add_response_handler(&block) ⇒ Object
451 452 453 |
# File 'lib/em-imap/client.rb', line 451 def add_response_handler(&block) @connection.add_response_handler(&block) end |
#append(mailbox, message, flags = nil, date_time = nil) ⇒ Object
Add a message to the mailbox.
The tagged response with which this command succeeds contains the UID of the email that was appended.
214 215 216 217 218 219 220 |
# File 'lib/em-imap/client.rb', line 214 def append(mailbox, , flags=nil, date_time=nil) args = [to_utf7(mailbox)] args << flags if flags args << date_time if date_time args << Net::IMAP::Literal.new() tagged_response("APPEND", *args) end |
#authenticate(auth_type, *args) ⇒ Object
Authenticate using a custom authenticator.
By default there are two custom authenticators available:
'LOGIN', username, password
'CRAM-MD5', username, password (see RFC 2195)
Though you can add new mechanisms using EM::IMAP.add_authenticator, see for example the gmail_xoauth gem.
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/em-imap/client.rb', line 91 def authenticate(auth_type, *args) # Extract these first so that any exceptions can be raised # before the command is created. auth_type = auth_type.to_s.upcase auth_handler = authenticator(auth_type, *args) tagged_response('AUTHENTICATE', auth_type).tap do |command| @connection.send_authentication_data(auth_handler, command) end end |
#capability ⇒ Object
Ask the server which capabilities it supports.
Succeeds with an array of capabilities.
32 33 34 |
# File 'lib/em-imap/client.rb', line 32 def capability one_data_response("CAPABILITY").transform{ |response| response.data } end |
#check ⇒ Object
Checkpoint the current mailbox.
This is an implementation-defined operation, when in doubt, NOOP should be used instead.
229 230 231 |
# File 'lib/em-imap/client.rb', line 229 def check tagged_response("CHECK") end |
#close ⇒ Object
Unselect the current mailbox.
As a side-effect, permanently removes any messages that have the Deleted flag. (Unless the mailbox was selected using the EXAMINE, in which case no side effects occur).
239 240 241 |
# File 'lib/em-imap/client.rb', line 239 def close tagged_response("CLOSE") end |
#connect ⇒ Object
14 15 16 17 18 19 20 |
# File 'lib/em-imap/client.rb', line 14 def connect @connection = EM::IMAP::Connection.connect(*@connect_args) @connection.errback{ |e| fail e }. callback{ |*args| succeed *args } @connection.hello_listener end |
#copy(seq, mailbox) ⇒ Object
Copy the specified messages to another mailbox.
347 348 349 |
# File 'lib/em-imap/client.rb', line 347 def copy(seq, mailbox) tagged_response("COPY", Net::IMAP::MessageSet.new(seq), to_utf7(mailbox)) end |
#create(mailbox) ⇒ Object
Create a new mailbox with the given name.
136 137 138 |
# File 'lib/em-imap/client.rb', line 136 def create(mailbox) tagged_response("CREATE", to_utf7(mailbox)) end |
#delete(mailbox) ⇒ Object
Delete the mailbox with this name.
142 143 144 |
# File 'lib/em-imap/client.rb', line 142 def delete(mailbox) tagged_response("DELETE", to_utf7(mailbox)) end |
#disconnect ⇒ Object
22 23 24 |
# File 'lib/em-imap/client.rb', line 22 def disconnect @connection.close_connection end |
#examine(mailbox) ⇒ Object
Select a mailbox for performing read-only commands.
This is exactly the same as select, except that no operation may cause a change to the state of the mailbox or its messages.
130 131 132 |
# File 'lib/em-imap/client.rb', line 130 def examine(mailbox) tagged_response("EXAMINE", to_utf7(mailbox)) end |
#expunge ⇒ Object
Permanently remove any messages with the Deleted flag from the current mailbox.
Succeeds with a list of message sequence numbers that were deleted.
NOTE: If you’re planning to EXPUNGE and then SELECT a new mailbox, and you don’t care which messages are removed, consider using CLOSE instead.
252 253 254 255 256 |
# File 'lib/em-imap/client.rb', line 252 def expunge multi_data_response("EXPUNGE").transform do |untagged_responses| untagged_responses.map(&:data) end end |
#fetch(seq, attr = "FULL") ⇒ Object
Get the contents of, or information about, a message.
Possible attribute names (see RFC 3501 for a full list):
ALL: Gets all header information,
FULL: Same as ALL with the addition of the BODY,
FAST: Same as ALL without the message envelope.
BODY: The body
BODY[<section>] A particular section of the body
BODY[<section>]<<start>,<length>> A substring of a section of the body.
BODY.PEEK: The body (but doesn't change the \Recent flag)
FLAGS: The flags
INTERNALDATE: The internal date
UID: The unique identifier
316 317 318 |
# File 'lib/em-imap/client.rb', line 316 def fetch(seq, attr="FULL") fetch_internal("FETCH", seq, attr) end |
#idle(&block) ⇒ Object
The IDLE command allows you to wait for any untagged responses that give status updates about the contents of a mailbox.
Until you call stop on the idler, no further commands can be sent over this connection.
idler = connection.idle do |untagged_response|
case untagged_response.name
#...
end
end
EM.timeout(60) { idler.stop }
371 372 373 374 375 376 |
# File 'lib/em-imap/client.rb', line 371 def idle(&block) send_command("IDLE").tap do |command| @connection.prepare_idle_continuation(command) command.listen(&block) if block_given? end end |
#list(refname = "", pattern = "*") ⇒ Object
List all available mailboxes.
@param: refname, an optional context in which to list. @param: mailbox, a which mailboxes to return.
Succeeds with a list of Net::IMAP::MailboxList structs, each of which has:
.name, the name of the mailbox (in UTF8)
.delim, the delimeter (normally "/")
.attr, A list of attributes, e.g. :Noselect, :Haschildren, :Hasnochildren.
174 175 176 |
# File 'lib/em-imap/client.rb', line 174 def list(refname="", pattern="*") list_internal("LIST", refname, pattern) end |
#login(username, password) ⇒ Object
Authenticate with a username and password.
NOTE: this SHOULD only work over a tls connection.
If the password is wrong, the command will fail with a Net::IMAP::NoResponseError.
109 110 111 |
# File 'lib/em-imap/client.rb', line 109 def login(username, password) tagged_response("LOGIN", username, password) end |
#logout ⇒ Object
Logout and close the connection.
This will cause any other listeners or commands that are still active to fail, and render this client unusable.
52 53 54 55 56 57 58 59 60 |
# File 'lib/em-imap/client.rb', line 52 def logout command = tagged_response("LOGOUT").errback do |e| if e.is_a? Net::IMAP::ByeResponseError # RFC 3501 says the server MUST send a BYE response and then close the connection. disconnect command.succeed end end end |
#lsub(refname, pattern) ⇒ Object
List all subscribed mailboxes.
This is the same as list, but restricted to mailboxes that have been subscribed to.
182 183 184 |
# File 'lib/em-imap/client.rb', line 182 def lsub(refname, pattern) list_internal("LSUB", refname, pattern) end |
#noop ⇒ Object
Actively do nothing.
This is useful as a keep-alive, or to persuade the server to send any untagged responses your listeners would like.
Succeeds with nil.
43 44 45 |
# File 'lib/em-imap/client.rb', line 43 def noop tagged_response("NOOP") end |
#rename(oldname, newname) ⇒ Object
Rename the mailbox with this name.
148 149 150 |
# File 'lib/em-imap/client.rb', line 148 def rename(oldname, newname) tagged_response("RENAME", to_utf7(oldname), to_utf7(newname)) end |
#search(*args) ⇒ Object
Search for messages in the current mailbox.
269 270 271 |
# File 'lib/em-imap/client.rb', line 269 def search(*args) search_internal("SEARCH", *args) end |
#select(mailbox) ⇒ Object
Select a mailbox for performing commands against.
This will generate untagged responses that you can subscribe to by adding a block to the listener with .listen, for more detail, see RFC 3501, section 6.3.1.
121 122 123 |
# File 'lib/em-imap/client.rb', line 121 def select(mailbox) tagged_response("SELECT", to_utf7(mailbox)) end |
#sort(sort_keys, *args) ⇒ Object
SORT and THREAD (like SEARCH) from tools.ietf.org/search/rfc5256
281 282 283 |
# File 'lib/em-imap/client.rb', line 281 def sort(sort_keys, *args) raise NotImplementedError end |
#starttls ⇒ Object Also known as: start_tls
Run a STARTTLS handshake.
C: “STARTTLSrn” S: “OK go aheadrn” C: <tls handshake> S: <tls handshake>
Succeeds with the OK response after the TLS handshake is complete.
72 73 74 75 76 |
# File 'lib/em-imap/client.rb', line 72 def starttls tagged_response("STARTTLS").bind! do |response| @connection.start_tls.transform{ response } end end |
#status(mailbox, attrs = ['MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN']) ⇒ Object
Get the status of a mailbox.
This provides similar information to the untagged responses you would get by running SELECT or EXAMINE without doing so.
Succeeds with a hash of attribute name to value returned by the server.
197 198 199 200 201 202 |
# File 'lib/em-imap/client.rb', line 197 def status(mailbox, attrs=['MESSAGES', 'RECENT', 'UIDNEXT', 'UIDVALIDITY', 'UNSEEN']) attrs = [attrs] if attrs.is_a?(String) one_data_response("STATUS", to_utf7(mailbox), attrs).transform do |response| response.data.attr end end |
#store(seq, name, value) ⇒ Object
Update the flags on a message.
The .SILENT versions suppress the server's responses.
335 336 337 |
# File 'lib/em-imap/client.rb', line 335 def store(seq, name, value) store_internal("STORE", seq, name, value) end |
#subscribe(mailbox) ⇒ Object
Add this mailbox to the list of subscribed mailboxes.
154 155 156 |
# File 'lib/em-imap/client.rb', line 154 def subscribe(mailbox) tagged_response("SUBSCRIBE", to_utf7(mailbox)) end |
#thread(algorithm, *args) ⇒ Object
289 290 291 |
# File 'lib/em-imap/client.rb', line 289 def thread(algorithm, *args) raise NotImplementedError end |
#uid_copy(seq, mailbox) ⇒ Object
The same as copy, but keyed off UIDs instead of sequence numbers.
353 354 355 |
# File 'lib/em-imap/client.rb', line 353 def uid_copy(seq, mailbox) tagged_response("UID", "COPY", Net::IMAP::MessageSet.new(seq), to_utf7(mailbox)) end |
#uid_fetch(seq, attr = "FULL") ⇒ Object
The same as fetch, but keyed of UIDs instead of sequence numbers.
322 323 324 |
# File 'lib/em-imap/client.rb', line 322 def uid_fetch(seq, attr="FULL") fetch_internal("UID FETCH", seq, attr) end |
#uid_search(*args) ⇒ Object
The same as search, but succeeding with a list of UIDs not sequence numbers.
275 276 277 |
# File 'lib/em-imap/client.rb', line 275 def uid_search(*args) search_internal("UID SEARCH", *args) end |
#uid_sort(sort_keys, *args) ⇒ Object
285 286 287 |
# File 'lib/em-imap/client.rb', line 285 def uid_sort(sort_keys, *args) raise NotImplementedError end |
#uid_store(seq, name, value) ⇒ Object
The same as store, but keyed off UIDs instead of sequence numbers.
341 342 343 |
# File 'lib/em-imap/client.rb', line 341 def uid_store(seq, name, value) store_internal("UID", "STORE", seq, name, value) end |
#uid_thread(algorithm, *args) ⇒ Object
293 294 295 |
# File 'lib/em-imap/client.rb', line 293 def uid_thread(algorithm, *args) raise NotImplementedError end |
#unsubscribe(mailbox) ⇒ Object
Remove this mailbox from the list of subscribed mailboxes.
160 161 162 |
# File 'lib/em-imap/client.rb', line 160 def unsubscribe(mailbox) tagged_response("UNSUBSCRIBE", to_utf7(mailbox)) end |
#wait_for_new_emails(wrapper = Listener.new, &block) ⇒ Object
Wait for new emails to arrive, and call the block when they do.
This method will run until the upstream connection is closed, re-idling after every 29 minutes as implied by the IMAP spec. If you want to stop it, call .stop on the returned listener
idler = client.wait_for_new_emails do |exists_response, &stop_waiting|
client.fetch(exists_response.data).bind! do |response|
puts response
end
end
idler.stop
NOTE: the block should return a deferrable that succeeds when you are done processing the exists_response. At that point, the idler will be turned back on again.
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/em-imap/client.rb', line 433 def wait_for_new_emails(wrapper=Listener.new, &block) wait_for_one_email.listen do |response| wrapper.receive_event response end.bind! do |response| block.call response if response end.bind! do if wrapper.stopped? wrapper.succeed else wait_for_new_emails(wrapper, &block) end end.errback do |*e| wrapper.fail *e end wrapper end |
#wait_for_one_email(timeout = 29 * 60) ⇒ Object
A Wrapper around the IDLE command that lets you wait until one email is received
Returns a deferrable that succeeds when the IDLE command succeeds, or fails when the IDLE command fails.
If a new email has arrived, the deferrable will succeed with the EXISTS response, otherwise it will succeed with nil.
client.wait_for_one_email.bind! do |response|
process_new_email(response) if response
end
This method will be default wait for 29minutes as suggested by the IMAP spec.
WARNING: just as with IDLE, no further commands can be sent over this connection until this deferrable has succeeded. You can stop it ahead of time if needed by calling stop on the returned deferrable.
idler = client.wait_for_one_email.bind! do |response|
process_new_email(response) if response
end idler.stop
See also #wait_for_new_emails
403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/em-imap/client.rb', line 403 def wait_for_one_email(timeout=29 * 60) exists_response = nil idler = idle EM::Timer.new(timeout) { idler.stop } idler.listen do |response| if Net::IMAP::UntaggedResponse === response && response.name =~ /\AEXISTS\z/i exists_response = response idler.stop end end.transform{ exists_response } end |