Module: EventMachine::IMAP::CommandSender

Includes:
LineBuffer, ContinuationSynchronisation
Included in:
Connection
Defined in:
lib/em-imap/command_sender.rb

Overview

Used to send commands, and various other pieces of data, to the IMAP server as they are needed. Plugs in the ContinuationSynchronisation module so that the outgoing channel is free of racey-behaviour.

Defined Under Namespace

Modules: LineBuffer

Instance Method Summary collapse

Methods included from ContinuationSynchronisation

#await_continuations, #awaiting_continuation?, #listen_for_continuation, #post_init, #when_not_awaiting_continuation

Methods included from LineBuffer

#post_init, #send_line_buffered

Instance Method Details

#prepare_idle_continuation(command) ⇒ Object

Register a stopback on the IDLE command that sends the DONE continuation that the server is waiting for.

This blocks the outgoing connection until the IDLE command is stopped, as required by RFC 2177.

Parameters:

  • command,

    The IDLE command.



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/em-imap/command_sender.rb', line 117

def prepare_idle_continuation(command)
  when_not_awaiting_continuation do
    waiter = await_continuations
    command.stopback do
      waiter.stop
      begin
        send_data "DONE\r\n"
      rescue => e
        command.fail e
      end
    end
  end
end

#send_authentication_data(auth_handler, command) ⇒ Object

Pass a challenge/response between the server and the auth_handler.

This can be called several times in one authorization handshake depending on how many messages the server wishes to see from the auth_handler.

If the auth_handler raises an exception, or the network connection dies for some reason, the command will be failed.

Parameters:

  • auth_handler,

    an authorization handler.

  • command,

    the associated AUTHORIZE command.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/em-imap/command_sender.rb', line 94

def send_authentication_data(auth_handler, command)
  when_not_awaiting_continuation do
    waiter = await_continuations do |response|
      begin
        data = auth_handler.process(response.data.text.unpack("m")[0])
        s = [data].pack("m").gsub(/\n/, "")
        send_data(s + CRLF)
      rescue => e
        command.fail e
      end
    end
    command.bothback{ |*args| waiter.stop }
  end
end

#send_command_object(command) ⇒ Object

Send a command to the IMAP server.

This method has two phases, the first of which is to convert your command into tokens for sending over the network, and the second is to actually send those fragments.

If the conversion fails, a Net::IMAP::DataFormatError will be raised which you should handle synchronously. If the sending fails, then the command will be failed asynchronously.

Parameters:

  • command,

    The command to send.



19
20
21
22
23
24
25
26
27
# File 'lib/em-imap/command_sender.rb', line 19

def send_command_object(command)
  Formatter.format(command) do |to_send|
    if to_send.is_a? Formatter::Literal
      send_literal to_send.str, command
    else
      send_string  to_send, command
    end
  end
end

#send_literal(literal, command) ⇒ Object

Send an IMAP literal to the server.

Sending literals is a somewhat complicated process:

Step 1. Client tells the server how big the literal will be.

(and at the same time shows the server the contents of the command so
far)

Step 2. The server either accepts (with a ContinuationResponse) or

rejects (with a BadResponse) the continuation based on the size of the
literal, and the validity of the line so far.

Step 3. The client sends the literal, followed by a linefeed, and then continues with sending the rest of the command.

Parameters:

  • literal,

    the string to send.

  • command,

    the command associated with this string.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/em-imap/command_sender.rb', line 63

def send_literal(literal, command)
  when_not_awaiting_continuation do
    begin
      send_line_buffered "{" + literal.size.to_s + "}" + CRLF
    rescue => e
      command.fail e
    end
    waiter = await_continuations do
      begin
        send_data literal
      rescue => e
        command.fail e
      end
      waiter.stop
    end
    command.errback{ waiter.stop }
  end
end

#send_string(str, command) ⇒ Object

Send some normal (binary/string) data to the server.

This uses the LineBuffer, and fails the command if the network connection has died for some reason.

Parameters:

  • str,

    the data to send

  • command,

    the command for which the data is being sent.



37
38
39
40
41
42
43
44
45
# File 'lib/em-imap/command_sender.rb', line 37

def send_string(str, command)
  when_not_awaiting_continuation do
    begin
      send_line_buffered str
    rescue => e
      command.fail e
    end
  end
end