em-imap 0.1.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of em-imap might be problematic. Click here for more details.
- data/README.md +17 -12
- data/lib/em-imap/client.rb +10 -2
- data/lib/em-imap/command_sender.rb +94 -57
- data/lib/em-imap/connection.rb +56 -46
- data/lib/em-imap/continuation_synchronisation.rb +1 -1
- data/lib/em-imap/formatter.rb +124 -0
- data/lib/em-imap/listener.rb +0 -1
- data/lib/em-imap/response_parser.rb +1 -4
- data/lib/em-imap.rb +5 -0
- metadata +4 -4
data/README.md
CHANGED
@@ -10,7 +10,7 @@ This document tries to introduce concepts of IMAP alongside the facilities of th
|
|
10
10
|
|
11
11
|
### Connecting
|
12
12
|
|
13
|
-
Before you can communicate with an IMAP server, you must first connect to it. There are three connection parameters, the hostname, the port number, and whether to use SSL/TLS. As with every method in EM::IMAP, `EM::IMAP
|
13
|
+
Before you can communicate with an IMAP server, you must first connect to it. There are three connection parameters, the hostname, the port number, and whether to use SSL/TLS. As with every method in EM::IMAP, `EM::IMAP::Client#connect` returns a [deferrable](http://eventmachine.rubyforge.org/docs/DEFERRABLES.html) enhanced by the [deferrable\_gratification](https://github.com/samstokes/deferrable_gratification) library.
|
14
14
|
|
15
15
|
For example, to connect to Gmail's IMAP server, you can use the following snippet:
|
16
16
|
|
@@ -18,8 +18,8 @@ For example, to connect to Gmail's IMAP server, you can use the following snippe
|
|
18
18
|
require 'em-imap'
|
19
19
|
|
20
20
|
EM::run do
|
21
|
-
client = EM::IMAP.
|
22
|
-
client.errback do |error|
|
21
|
+
client = EM::IMAP.new('imap.gmail.com', 993, true)
|
22
|
+
client.connect.errback do |error|
|
23
23
|
puts "Connecting failed: #{error}"
|
24
24
|
end.callback do |hello_response|
|
25
25
|
puts "Connecting succeeded!"
|
@@ -34,8 +34,8 @@ There are two authentication mechanisms in IMAP, `LOGIN` and `AUTHENTICATE`, exp
|
|
34
34
|
|
35
35
|
Extending our previous example to also log in to Gmail:
|
36
36
|
|
37
|
-
client = EM::IMAP.
|
38
|
-
client.bind! do
|
37
|
+
client = EM::IMAP.new('imap.gmail.com', 993, true)
|
38
|
+
client.connect.bind! do
|
39
39
|
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
|
40
40
|
end.callback do
|
41
41
|
puts "Connected and logged in!"
|
@@ -49,8 +49,8 @@ The `.authenticate` method is more advanced and uses the same extensible mechani
|
|
49
49
|
|
50
50
|
Once the authentication has completed successfully, you can perform IMAP commands that don't require a currently selected mailbox. For example to get a list of the names of all Gmail mailboxes (including labels):
|
51
51
|
|
52
|
-
client = EM::IMAP.
|
53
|
-
client.bind! do
|
52
|
+
client = EM::IMAP.new('imap.gmail.com', 993, true)
|
53
|
+
client.connect.bind! do
|
54
54
|
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
|
55
55
|
end.bind! do
|
56
56
|
client.list
|
@@ -68,8 +68,8 @@ In order to do useful things which actual messages, you need to first select a m
|
|
68
68
|
|
69
69
|
For example to search for all emails relevant to em-imap in Gmail:
|
70
70
|
|
71
|
-
client = EM::IMAP.
|
72
|
-
client.bind! do
|
71
|
+
client = EM::IMAP.new('imap.gmail.com', 993, true)
|
72
|
+
client.connect.bind! do
|
73
73
|
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
|
74
74
|
end.bind! do
|
75
75
|
client.select('[Google Mail]/All Mail')
|
@@ -83,8 +83,8 @@ For example to search for all emails relevant to em-imap in Gmail:
|
|
83
83
|
|
84
84
|
Once you have a list of message sequence numbers, as returned by search, you can actually read the emails with `.fetch`:
|
85
85
|
|
86
|
-
client = EM::IMAP.
|
87
|
-
client.bind! do
|
86
|
+
client = EM::IMAP.new('imap.gmail.com', 993, true)
|
87
|
+
client.connect.bind! do
|
88
88
|
client.login("conrad.irwin@gmail.com", ENV["GMAIL_PASSWORD"])
|
89
89
|
end.bind! do
|
90
90
|
client.select('[Google Mail]/All Mail')
|
@@ -148,7 +148,8 @@ If you want to receive server responses at any time, you can call `.add_response
|
|
148
148
|
|
149
149
|
If you want to send commands without waiting for previous replies, you can also do so. em-imap handles the few cases where this is not permitted (for example, during an IDLE command) by queueing the command until the connection becomes available again. If you do this, bear in mind that any blocks that are listening on the connection may receive responses from multiple commands interleaved.
|
150
150
|
|
151
|
-
client = EM::Imap.
|
151
|
+
client = EM::Imap.new('imap.gmail.com', 993, true)
|
152
|
+
client.connect.callback do
|
152
153
|
logger_in = client.login('conrad.irwin@gmail.com', ENV["GMAIL_PASSWORD"])
|
153
154
|
selecter = client.select('[Google Mail]/All Mail')
|
154
155
|
searcher = client.search('from:conrad@rapportive.com').callback do |results|
|
@@ -172,6 +173,10 @@ Before version 1, at least the following changes should be made:
|
|
172
173
|
4. Support SORT and THREAD.
|
173
174
|
5. Put the in-line documentation into a real format.
|
174
175
|
|
176
|
+
### Breaking Changes
|
177
|
+
|
178
|
+
Between Version 0.1(.x) and 0.2, the connection setup API changed. Previously you would call `EM::IMAP.connect`, now that is broken into two steps: `EM::IMAP.new` and `EM::IMAP::Client#connect` as documented above. This makes it less likely people will write `client = connect.bind!` by accident, and allows you to bind to the `errback` of the connection as a whole should you wish to.
|
179
|
+
|
175
180
|
## Meta-foo
|
176
181
|
|
177
182
|
Em-imap is made available under the MIT license, see LICENSE.MIT for details
|
data/lib/em-imap/client.rb
CHANGED
@@ -7,8 +7,16 @@ module EventMachine
|
|
7
7
|
|
8
8
|
include IMAP::Authenticators
|
9
9
|
|
10
|
-
def initialize(
|
11
|
-
@
|
10
|
+
def initialize(host, port, usessl=false)
|
11
|
+
@connect_args=[host, port, usessl]
|
12
|
+
end
|
13
|
+
|
14
|
+
def connect
|
15
|
+
@connection = EM::IMAP::Connection.connect(*@connect_args)
|
16
|
+
@connection.errback{ |e| fail e }.
|
17
|
+
callback{ |*args| succeed *args }
|
18
|
+
|
19
|
+
@connection.hello_listener
|
12
20
|
end
|
13
21
|
|
14
22
|
def disconnect
|
@@ -1,44 +1,96 @@
|
|
1
1
|
module EventMachine
|
2
2
|
module IMAP
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Used to send commands, and various other pieces of data, to the IMAP
|
4
|
+
# server as they are needed. Plugs in the ContinuationSynchronisation module
|
5
|
+
# so that the outgoing channel is free of racey-behaviour.
|
5
6
|
module CommandSender
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
# Send a command to the IMAP server.
|
8
|
+
#
|
9
|
+
# @param command, The command to send.
|
10
|
+
#
|
11
|
+
# This method has two phases, the first of which is to convert your
|
12
|
+
# command into tokens for sending over the network, and the second is to
|
13
|
+
# actually send those fragments.
|
14
|
+
#
|
15
|
+
# If the conversion fails, a Net::IMAP::DataFormatError will be raised
|
16
|
+
# which you should handle synchronously. If the sending fails, then the
|
17
|
+
# command will be failed asynchronously.
|
18
|
+
#
|
19
|
+
def send_command_object(command)
|
20
|
+
Formatter.format(command) do |to_send|
|
21
|
+
if to_send.is_a? Formatter::Literal
|
22
|
+
send_literal to_send.str, command
|
23
|
+
else
|
24
|
+
send_string to_send, command
|
25
|
+
end
|
16
26
|
end
|
27
|
+
end
|
17
28
|
|
18
|
-
|
19
|
-
|
29
|
+
# Send some normal (binary/string) data to the server.
|
30
|
+
#
|
31
|
+
# @param str, the data to send
|
32
|
+
# @param command, the command for which the data is being sent.
|
33
|
+
#
|
34
|
+
# This uses the LineBuffer, and fails the command if the network
|
35
|
+
# connection has died for some reason.
|
36
|
+
#
|
37
|
+
def send_string(str, command)
|
38
|
+
when_not_awaiting_continuation do
|
39
|
+
begin
|
40
|
+
send_line_buffered str
|
41
|
+
rescue => e
|
42
|
+
command.fail e
|
43
|
+
end
|
20
44
|
end
|
21
|
-
|
22
|
-
public :send_data
|
23
45
|
end
|
24
46
|
|
25
|
-
#
|
26
|
-
# of string.
|
47
|
+
# Send an IMAP literal to the server.
|
27
48
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
49
|
+
# @param literal, the string to send.
|
50
|
+
# @param command, the command associated with this string.
|
51
|
+
#
|
52
|
+
# Sending literals is a somewhat complicated process:
|
53
|
+
#
|
54
|
+
# Step 1. Client tells the server how big the literal will be.
|
55
|
+
# (and at the same time shows the server the contents of the command so
|
56
|
+
# far)
|
57
|
+
# Step 2. The server either accepts (with a ContinuationResponse) or
|
58
|
+
# rejects (with a BadResponse) the continuation based on the size of the
|
59
|
+
# literal, and the validity of the line so far.
|
60
|
+
# Step 3. The client sends the literal, followed by a linefeed, and then
|
61
|
+
# continues with sending the rest of the command.
|
62
|
+
#
|
63
|
+
def send_literal(literal, command)
|
64
|
+
when_not_awaiting_continuation do
|
65
|
+
begin
|
66
|
+
send_line_buffered "{" + literal.size.to_s + "}" + CRLF
|
67
|
+
rescue => e
|
68
|
+
command.fail e
|
69
|
+
end
|
70
|
+
waiter = await_continuations do
|
71
|
+
begin
|
72
|
+
send_data literal
|
73
|
+
rescue => e
|
74
|
+
command.fail e
|
75
|
+
end
|
76
|
+
waiter.stop
|
77
|
+
end
|
78
|
+
command.errback{ waiter.stop }
|
37
79
|
end
|
38
|
-
sender.put_string CRLF
|
39
80
|
end
|
40
81
|
|
41
|
-
#
|
82
|
+
# Pass a challenge/response between the server and the auth_handler.
|
83
|
+
#
|
84
|
+
# @param auth_handler, an authorization handler.
|
85
|
+
# @param command, the associated AUTHORIZE command.
|
86
|
+
#
|
87
|
+
# This can be called several times in one authorization handshake
|
88
|
+
# depending on how many messages the server wishes to see from the
|
89
|
+
# auth_handler.
|
90
|
+
#
|
91
|
+
# If the auth_handler raises an exception, or the network connection dies
|
92
|
+
# for some reason, the command will be failed.
|
93
|
+
#
|
42
94
|
def send_authentication_data(auth_handler, command)
|
43
95
|
when_not_awaiting_continuation do
|
44
96
|
waiter = await_continuations do |response|
|
@@ -54,6 +106,14 @@ module EventMachine
|
|
54
106
|
end
|
55
107
|
end
|
56
108
|
|
109
|
+
# Register a stopback on the IDLE command that sends the DONE
|
110
|
+
# continuation that the server is waiting for.
|
111
|
+
#
|
112
|
+
# @param command, The IDLE command.
|
113
|
+
#
|
114
|
+
# This blocks the outgoing connection until the IDLE command is stopped,
|
115
|
+
# as required by RFC 2177.
|
116
|
+
#
|
57
117
|
def prepare_idle_continuation(command)
|
58
118
|
when_not_awaiting_continuation do
|
59
119
|
waiter = await_continuations
|
@@ -68,35 +128,12 @@ module EventMachine
|
|
68
128
|
end
|
69
129
|
end
|
70
130
|
|
71
|
-
def send_string(str, command)
|
72
|
-
when_not_awaiting_continuation do
|
73
|
-
begin
|
74
|
-
send_line_buffered str
|
75
|
-
rescue => e
|
76
|
-
command.fail e
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def send_literal(literal, command)
|
82
|
-
when_not_awaiting_continuation do
|
83
|
-
begin
|
84
|
-
send_line_buffered "{" + literal.size.to_s + "}" + CRLF
|
85
|
-
rescue => e
|
86
|
-
command.fail e
|
87
|
-
end
|
88
|
-
waiter = await_continuations do
|
89
|
-
begin
|
90
|
-
send_data literal
|
91
|
-
rescue => e
|
92
|
-
command.fail e
|
93
|
-
end
|
94
|
-
waiter.stop
|
95
|
-
end
|
96
|
-
command.errback{ waiter.stop }
|
97
|
-
end
|
98
|
-
end
|
99
131
|
|
132
|
+
# Buffers out-going string sending by-line.
|
133
|
+
#
|
134
|
+
# This is safe to do for IMAP because the client always ends transmission
|
135
|
+
# on a CRLF (for awaiting continuation requests, and for ending commands)
|
136
|
+
#
|
100
137
|
module LineBuffer
|
101
138
|
def post_init
|
102
139
|
super
|
data/lib/em-imap/connection.rb
CHANGED
@@ -25,6 +25,38 @@ module EventMachine
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
def post_init
|
29
|
+
@listeners = []
|
30
|
+
super
|
31
|
+
listen_for_failure
|
32
|
+
listen_for_greeting
|
33
|
+
end
|
34
|
+
|
35
|
+
# This listens for the IMAP connection to have been set up. This should
|
36
|
+
# be shortly after the TCP connection is available, once we've received
|
37
|
+
# a greeting from the server.
|
38
|
+
def listen_for_greeting
|
39
|
+
add_to_listener_pool(hello_listener)
|
40
|
+
hello_listener.listen do |response|
|
41
|
+
# TODO: Is this the right condition? I think it can be one of several
|
42
|
+
# possible answers depending on how trusted the connection is, but probably
|
43
|
+
# not *anything* except BYE.
|
44
|
+
if response.is_a?(Net::IMAP::UntaggedResponse) && response.name != "BYE"
|
45
|
+
hello_listener.succeed response
|
46
|
+
else
|
47
|
+
hello_listener.fail Net::IMAP::ResponseParseError.new(response.raw_data)
|
48
|
+
end
|
49
|
+
end.errback do |e|
|
50
|
+
hello_listener.fail e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns a Listener that is active during connection setup, and which is succeeded
|
55
|
+
# or failed as soon as we've received a greeting from the server.
|
56
|
+
def hello_listener
|
57
|
+
@hello_listener ||= Listener.new.errback{ |e| fail e }.bothback{ hello_listener.stop }
|
58
|
+
end
|
59
|
+
|
28
60
|
# Send the command, with the given arguments, to the IMAP server.
|
29
61
|
#
|
30
62
|
# @param cmd, the name of the command to send (a string)
|
@@ -48,7 +80,6 @@ module EventMachine
|
|
48
80
|
Command.new(next_tag!, cmd, args).tap do |command|
|
49
81
|
add_to_listener_pool(command)
|
50
82
|
listen_for_tagged_response(command)
|
51
|
-
listen_for_bye_response(command)
|
52
83
|
send_command_object(command)
|
53
84
|
end
|
54
85
|
end
|
@@ -67,49 +98,9 @@ module EventMachine
|
|
67
98
|
Listener.new(&block).tap do |listener|
|
68
99
|
listener.stopback{ listener.succeed }
|
69
100
|
add_to_listener_pool(listener)
|
70
|
-
listen_for_bye_response(listener)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def post_init
|
75
|
-
@listeners = Set.new
|
76
|
-
super
|
77
|
-
listen_for_greeting
|
78
|
-
end
|
79
|
-
|
80
|
-
# Listen for the first response from the server and succeed or fail
|
81
|
-
# the connection deferrable.
|
82
|
-
def listen_for_greeting
|
83
|
-
hello_listener = add_response_handler do |response|
|
84
|
-
hello_listener.stop
|
85
|
-
if response.is_a?(Net::IMAP::UntaggedResponse)
|
86
|
-
if response.name == "BYE"
|
87
|
-
fail Net::IMAP::ByeResponseError.new(response.raw_data)
|
88
|
-
else
|
89
|
-
succeed response
|
90
|
-
end
|
91
|
-
else
|
92
|
-
fail Net::IMAP::ResponseParseError.new(response.raw_data)
|
93
|
-
end
|
94
|
-
end.errback do |e|
|
95
|
-
fail e
|
96
101
|
end
|
97
102
|
end
|
98
103
|
|
99
|
-
# Called when the connection is closed.
|
100
|
-
# TODO: Figure out how to send a useful error...
|
101
|
-
def unbind
|
102
|
-
fail_all EOFError.new("Connection to IMAP server was unbound"), true
|
103
|
-
end
|
104
|
-
|
105
|
-
def fail_all(error, closed=false)
|
106
|
-
# NOTE: Take a shallow clone of the listeners here so that we get guaranteed
|
107
|
-
# behaviour. We want to fail any listeners that may be added by the errbacks
|
108
|
-
# of other listeners.
|
109
|
-
@listeners.clone.each{ |listener| listener.fail error } while @listeners.size > 0
|
110
|
-
close_connection unless closed
|
111
|
-
end
|
112
|
-
|
113
104
|
def add_to_listener_pool(listener)
|
114
105
|
@listeners << listener.bothback{ @listeners.delete listener }
|
115
106
|
end
|
@@ -140,12 +131,31 @@ module EventMachine
|
|
140
131
|
end
|
141
132
|
end
|
142
133
|
|
143
|
-
#
|
144
|
-
#
|
145
|
-
def
|
146
|
-
|
134
|
+
# Called when the connection is closed.
|
135
|
+
# TODO: Figure out how to send a useful error...
|
136
|
+
def unbind
|
137
|
+
@unbound = true
|
138
|
+
fail EOFError.new("Connection to IMAP server was unbound")
|
139
|
+
end
|
140
|
+
|
141
|
+
# Attach life-long listeners on various conditions that we want to treat as connection
|
142
|
+
# errors. When such an error occurs, we want to fail all the currently pending commands
|
143
|
+
# so that the user of the library doesn't have to subscribe to more than one stream
|
144
|
+
# of errors.
|
145
|
+
def listen_for_failure
|
146
|
+
errback do |error|
|
147
|
+
# NOTE: Take a shallow clone of the listeners here so that we get guaranteed
|
148
|
+
# behaviour. We want to fail any listeners that may be added by the errbacks
|
149
|
+
# of other listeners.
|
150
|
+
@listeners.clone.each{ |listener| listener.fail error } while @listeners.size > 0
|
151
|
+
close_connection unless @unbound
|
152
|
+
end
|
153
|
+
|
154
|
+
# If we receive a BYE response from the server, then we're not going
|
155
|
+
# to hear any more, so we fail all our listeners.
|
156
|
+
add_response_handler do |response|
|
147
157
|
if response.is_a?(Net::IMAP::UntaggedResponse) && response.name == "BYE"
|
148
|
-
|
158
|
+
fail Net::IMAP::ByeResponseError.new(response.raw_data)
|
149
159
|
end
|
150
160
|
end
|
151
161
|
end
|
@@ -63,7 +63,7 @@ module EventMachine
|
|
63
63
|
if awaiting_continuation?
|
64
64
|
@awaiting_continuation.receive_event response
|
65
65
|
else
|
66
|
-
|
66
|
+
fail Net::IMAP::ResponseError.new("Unexpected continuation response from server")
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module EventMachine
|
2
|
+
module IMAP
|
3
|
+
class Formatter
|
4
|
+
|
5
|
+
# A placeholder so that the command sender knows to treat literal strings specially
|
6
|
+
class Literal < Struct.new(:str); end
|
7
|
+
|
8
|
+
# Format the data to be sent into strings and literals, and call the block
|
9
|
+
# for each token to be sent.
|
10
|
+
#
|
11
|
+
# @param data The data to format,
|
12
|
+
# @param &block The callback, which will be called with a number of strings and
|
13
|
+
# EM::IMAP::Formatter::Literal instances.
|
14
|
+
#
|
15
|
+
# NOTE: The block is responsible for handling any network-level concerns, such
|
16
|
+
# as sending literals only with permission.
|
17
|
+
#
|
18
|
+
def self.format(data, &block)
|
19
|
+
new(&block).send_data(data)
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(&block)
|
23
|
+
@block = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def put_string(str)
|
27
|
+
@block.call str
|
28
|
+
end
|
29
|
+
|
30
|
+
def send_literal(str)
|
31
|
+
@block.call Literal.new(str)
|
32
|
+
end
|
33
|
+
|
34
|
+
# The remainder of the code in this file is directly from Net::IMAP.
|
35
|
+
# Copyright (C) 2000 Shugo Maeda <shugo@ruby-lang.org>
|
36
|
+
def send_data(data)
|
37
|
+
case data
|
38
|
+
when nil
|
39
|
+
put_string("NIL")
|
40
|
+
when String
|
41
|
+
send_string_data(data)
|
42
|
+
when Integer
|
43
|
+
send_number_data(data)
|
44
|
+
when Array
|
45
|
+
send_list_data(data)
|
46
|
+
when Time
|
47
|
+
send_time_data(data)
|
48
|
+
when Symbol
|
49
|
+
send_symbol_data(data)
|
50
|
+
when EM::IMAP::Command
|
51
|
+
send_command(data)
|
52
|
+
else
|
53
|
+
data.send_data(self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_command(cmd)
|
58
|
+
put_string cmd.tag
|
59
|
+
put_string " "
|
60
|
+
put_string cmd.cmd
|
61
|
+
cmd.args.each do |i|
|
62
|
+
put_string " "
|
63
|
+
send_data(i)
|
64
|
+
end
|
65
|
+
put_string "\r\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
def send_string_data(str)
|
69
|
+
case str
|
70
|
+
when ""
|
71
|
+
put_string('""')
|
72
|
+
when /[\x80-\xff\r\n]/n
|
73
|
+
# literal
|
74
|
+
send_literal(str)
|
75
|
+
when /[(){ \x00-\x1f\x7f%*"\\]/n
|
76
|
+
# quoted string
|
77
|
+
send_quoted_string(str)
|
78
|
+
else
|
79
|
+
put_string(str)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def send_quoted_string(str)
|
84
|
+
put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"')
|
85
|
+
end
|
86
|
+
|
87
|
+
def send_number_data(num)
|
88
|
+
if num < 0 || num >= 4294967296
|
89
|
+
raise Net::IMAP::DataFormatError, num.to_s
|
90
|
+
end
|
91
|
+
put_string(num.to_s)
|
92
|
+
end
|
93
|
+
|
94
|
+
def send_list_data(list)
|
95
|
+
put_string("(")
|
96
|
+
first = true
|
97
|
+
list.each do |i|
|
98
|
+
if first
|
99
|
+
first = false
|
100
|
+
else
|
101
|
+
put_string(" ")
|
102
|
+
end
|
103
|
+
send_data(i)
|
104
|
+
end
|
105
|
+
put_string(")")
|
106
|
+
end
|
107
|
+
|
108
|
+
DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
|
109
|
+
|
110
|
+
def send_time_data(time)
|
111
|
+
t = time.dup.gmtime
|
112
|
+
s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"',
|
113
|
+
t.day, DATE_MONTH[t.month - 1], t.year,
|
114
|
+
t.hour, t.min, t.sec)
|
115
|
+
put_string(s)
|
116
|
+
end
|
117
|
+
|
118
|
+
def send_symbol_data(symbol)
|
119
|
+
put_string("\\" + symbol.to_s)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/em-imap/listener.rb
CHANGED
@@ -39,15 +39,12 @@ module EventMachine
|
|
39
39
|
# Callback used by receive data.
|
40
40
|
def receive_response(response); end
|
41
41
|
|
42
|
-
# Callback used if something goes wrong.
|
43
|
-
def fail_all(error); end
|
44
|
-
|
45
42
|
private
|
46
43
|
|
47
44
|
def parse(line)
|
48
45
|
@parser.parse(line)
|
49
46
|
rescue Net::IMAP::ResponseParseError => e
|
50
|
-
|
47
|
+
fail e
|
51
48
|
end
|
52
49
|
end
|
53
50
|
end
|
data/lib/em-imap.rb
CHANGED
@@ -8,6 +8,7 @@ require 'deferrable_gratification'
|
|
8
8
|
$:.unshift File.dirname( __FILE__ )
|
9
9
|
require 'em-imap/listener'
|
10
10
|
require 'em-imap/continuation_synchronisation'
|
11
|
+
require 'em-imap/formatter'
|
11
12
|
require 'em-imap/command_sender'
|
12
13
|
require 'em-imap/response_parser'
|
13
14
|
require 'em-imap/connection'
|
@@ -27,6 +28,10 @@ module EventMachine
|
|
27
28
|
Client.new(EventMachine::IMAP::Connection.connect(host, port, ssl))
|
28
29
|
end
|
29
30
|
|
31
|
+
def self.new(host, port, ssl=false)
|
32
|
+
Client.new(host, port, ssl)
|
33
|
+
end
|
34
|
+
|
30
35
|
class Command < Listener
|
31
36
|
attr_accessor :tag, :cmd, :args
|
32
37
|
def initialize(tag, cmd, args=[], &block)
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-imap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
|
10
|
-
version: 0.1.1
|
8
|
+
- 2
|
9
|
+
version: "0.2"
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Conrad Irwin
|
@@ -72,6 +71,7 @@ extra_rdoc_files: []
|
|
72
71
|
files:
|
73
72
|
- lib/em-imap.rb
|
74
73
|
- lib/em-imap/listener.rb
|
74
|
+
- lib/em-imap/formatter.rb
|
75
75
|
- lib/em-imap/command_sender.rb
|
76
76
|
- lib/em-imap/authenticators.rb
|
77
77
|
- lib/em-imap/connection.rb
|