eventmachine 0.12.6-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/.gitignore +13 -0
  2. data/Rakefile +254 -0
  3. data/docs/COPYING +60 -0
  4. data/docs/ChangeLog +211 -0
  5. data/docs/DEFERRABLES +138 -0
  6. data/docs/EPOLL +141 -0
  7. data/docs/GNU +281 -0
  8. data/docs/INSTALL +15 -0
  9. data/docs/KEYBOARD +38 -0
  10. data/docs/LEGAL +25 -0
  11. data/docs/LIGHTWEIGHT_CONCURRENCY +72 -0
  12. data/docs/PURE_RUBY +77 -0
  13. data/docs/README +74 -0
  14. data/docs/RELEASE_NOTES +96 -0
  15. data/docs/SMTP +9 -0
  16. data/docs/SPAWNED_PROCESSES +93 -0
  17. data/docs/TODO +10 -0
  18. data/eventmachine.gemspec +32 -0
  19. data/ext/binder.cpp +126 -0
  20. data/ext/binder.h +48 -0
  21. data/ext/cmain.cpp +586 -0
  22. data/ext/cplusplus.cpp +193 -0
  23. data/ext/ed.cpp +1522 -0
  24. data/ext/ed.h +380 -0
  25. data/ext/em.cpp +1937 -0
  26. data/ext/em.h +186 -0
  27. data/ext/emwin.cpp +300 -0
  28. data/ext/emwin.h +94 -0
  29. data/ext/epoll.cpp +26 -0
  30. data/ext/epoll.h +25 -0
  31. data/ext/eventmachine.h +98 -0
  32. data/ext/eventmachine_cpp.h +95 -0
  33. data/ext/extconf.rb +129 -0
  34. data/ext/fastfilereader/extconf.rb +77 -0
  35. data/ext/fastfilereader/mapper.cpp +214 -0
  36. data/ext/fastfilereader/mapper.h +59 -0
  37. data/ext/fastfilereader/rubymain.cpp +127 -0
  38. data/ext/files.cpp +94 -0
  39. data/ext/files.h +65 -0
  40. data/ext/kb.cpp +82 -0
  41. data/ext/page.cpp +107 -0
  42. data/ext/page.h +51 -0
  43. data/ext/pipe.cpp +351 -0
  44. data/ext/project.h +119 -0
  45. data/ext/rubymain.cpp +847 -0
  46. data/ext/sigs.cpp +89 -0
  47. data/ext/sigs.h +32 -0
  48. data/ext/ssl.cpp +423 -0
  49. data/ext/ssl.h +90 -0
  50. data/java/.classpath +8 -0
  51. data/java/.project +17 -0
  52. data/java/src/com/rubyeventmachine/Application.java +196 -0
  53. data/java/src/com/rubyeventmachine/Connection.java +74 -0
  54. data/java/src/com/rubyeventmachine/ConnectionFactory.java +37 -0
  55. data/java/src/com/rubyeventmachine/DefaultConnectionFactory.java +46 -0
  56. data/java/src/com/rubyeventmachine/EmReactor.java +408 -0
  57. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  58. data/java/src/com/rubyeventmachine/EventableChannel.java +57 -0
  59. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +171 -0
  60. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +244 -0
  61. data/java/src/com/rubyeventmachine/PeriodicTimer.java +38 -0
  62. data/java/src/com/rubyeventmachine/Timer.java +54 -0
  63. data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +108 -0
  64. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +124 -0
  65. data/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
  66. data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
  67. data/java/src/com/rubyeventmachine/tests/TestServers.java +74 -0
  68. data/java/src/com/rubyeventmachine/tests/TestTimers.java +89 -0
  69. data/lib/em/deferrable.rb +208 -0
  70. data/lib/em/eventable.rb +39 -0
  71. data/lib/em/future.rb +62 -0
  72. data/lib/em/messages.rb +66 -0
  73. data/lib/em/processes.rb +113 -0
  74. data/lib/em/spawnable.rb +88 -0
  75. data/lib/em/streamer.rb +112 -0
  76. data/lib/eventmachine.rb +1926 -0
  77. data/lib/eventmachine_version.rb +31 -0
  78. data/lib/evma.rb +32 -0
  79. data/lib/evma/callback.rb +32 -0
  80. data/lib/evma/container.rb +75 -0
  81. data/lib/evma/factory.rb +77 -0
  82. data/lib/evma/protocol.rb +87 -0
  83. data/lib/evma/reactor.rb +48 -0
  84. data/lib/jeventmachine.rb +137 -0
  85. data/lib/pr_eventmachine.rb +1011 -0
  86. data/lib/protocols/buftok.rb +127 -0
  87. data/lib/protocols/header_and_content.rb +129 -0
  88. data/lib/protocols/httpcli2.rb +803 -0
  89. data/lib/protocols/httpclient.rb +270 -0
  90. data/lib/protocols/line_and_text.rb +126 -0
  91. data/lib/protocols/linetext2.rb +161 -0
  92. data/lib/protocols/memcache.rb +293 -0
  93. data/lib/protocols/postgres.rb +261 -0
  94. data/lib/protocols/saslauth.rb +179 -0
  95. data/lib/protocols/smtpclient.rb +308 -0
  96. data/lib/protocols/smtpserver.rb +556 -0
  97. data/lib/protocols/stomp.rb +153 -0
  98. data/lib/protocols/tcptest.rb +57 -0
  99. data/setup.rb +1585 -0
  100. data/tasks/cpp.rake +77 -0
  101. data/tasks/project.rake +78 -0
  102. data/tasks/tests.rake +193 -0
  103. data/tests/test_attach.rb +83 -0
  104. data/tests/test_basic.rb +231 -0
  105. data/tests/test_connection_count.rb +45 -0
  106. data/tests/test_defer.rb +47 -0
  107. data/tests/test_epoll.rb +163 -0
  108. data/tests/test_error_handler.rb +35 -0
  109. data/tests/test_errors.rb +82 -0
  110. data/tests/test_eventables.rb +77 -0
  111. data/tests/test_exc.rb +58 -0
  112. data/tests/test_futures.rb +214 -0
  113. data/tests/test_handler_check.rb +37 -0
  114. data/tests/test_hc.rb +218 -0
  115. data/tests/test_httpclient.rb +215 -0
  116. data/tests/test_httpclient2.rb +155 -0
  117. data/tests/test_kb.rb +61 -0
  118. data/tests/test_ltp.rb +188 -0
  119. data/tests/test_ltp2.rb +320 -0
  120. data/tests/test_next_tick.rb +109 -0
  121. data/tests/test_processes.rb +95 -0
  122. data/tests/test_pure.rb +129 -0
  123. data/tests/test_running.rb +47 -0
  124. data/tests/test_sasl.rb +74 -0
  125. data/tests/test_send_file.rb +243 -0
  126. data/tests/test_servers.rb +80 -0
  127. data/tests/test_smtpclient.rb +83 -0
  128. data/tests/test_smtpserver.rb +93 -0
  129. data/tests/test_spawn.rb +329 -0
  130. data/tests/test_ssl_args.rb +68 -0
  131. data/tests/test_ssl_methods.rb +50 -0
  132. data/tests/test_timers.rb +148 -0
  133. data/tests/test_ud.rb +43 -0
  134. data/tests/testem.rb +31 -0
  135. data/web/whatis +7 -0
  136. metadata +207 -0
@@ -0,0 +1,179 @@
1
+ # $Id$
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+
27
+
28
+
29
+ module EventMachine
30
+ module Protocols
31
+
32
+ # Implements SASL authd.
33
+ # This is a very, very simple protocol that mimics the one used
34
+ # by saslauthd and pwcheck, two outboard daemons included in the
35
+ # standard SASL library distro.
36
+ # The only thing this is really suitable for is SASL PLAIN
37
+ # (user+password) authentication, but the SASL libs that are
38
+ # linked into standard servers (like imapd and sendmail) implement
39
+ # the other ones.
40
+ #
41
+ # SASL-auth is intended for reasonably fast operation inside a
42
+ # single machine, so it has no transport-security (although there
43
+ # have been multi-machine extensions incorporating transport-layer
44
+ # encryption).
45
+ #
46
+ # The standard saslauthd module generally runs privileged and does
47
+ # its work by referring to the system-account files.
48
+ #
49
+ # This feature was added to EventMachine to enable the development
50
+ # of custom authentication/authorization engines for standard servers.
51
+ #
52
+ # To use SASLauth, include it in a class that subclasses EM::Connection,
53
+ # and reimplement the validate method.
54
+ #
55
+ # The typical way to incorporate this module into an authentication
56
+ # daemon would be to set it as the handler for a UNIX-domain socket.
57
+ # The code might look like this:
58
+ #
59
+ # EM.start_unix_domain_server( "/var/run/saslauthd/mux", MyHandler )
60
+ # File.chmod( 0777, "/var/run/saslauthd/mux")
61
+ #
62
+ # The chmod is probably needed to ensure that unprivileged clients can
63
+ # access the UNIX-domain socket.
64
+ #
65
+ # It's also a very good idea to drop superuser privileges (if any), after
66
+ # the UNIX-domain socket has been opened.
67
+ #--
68
+ # Implementation details: assume the client can send us pipelined requests,
69
+ # and that the client will close the connection.
70
+ #
71
+ # The client sends us four values, each encoded as a two-byte length field in
72
+ # network order followed by the specified number of octets.
73
+ # The fields specify the username, password, service name (such as imap),
74
+ # and the "realm" name. We send back the barest minimum reply, a single
75
+ # field also encoded as a two-octet length in network order, followed by
76
+ # either "NO" or "OK" - simplicity itself.
77
+ #
78
+ # We enforce a maximum field size just as a sanity check.
79
+ # We do NOT automatically time out the connection.
80
+ #
81
+ # The code we use to parse out the values is ugly and probably slow.
82
+ # Improvements welcome.
83
+ #
84
+ module SASLauth
85
+
86
+ MaxFieldSize = 128*1024
87
+ def post_init
88
+ super
89
+ @sasl_data = ""
90
+ @sasl_values = []
91
+ end
92
+
93
+ def receive_data data
94
+ @sasl_data << data
95
+ while @sasl_data.length >= 2
96
+ len = (@sasl_data[0,2].unpack("n")).first
97
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
98
+ if @sasl_data.length >= (len + 2)
99
+ @sasl_values << @sasl_data[2,len]
100
+ @sasl_data.slice!(0...(2+len))
101
+ if @sasl_values.length == 4
102
+ send_data( validate(*@sasl_values) ? "\0\002OK" : "\0\002NO" )
103
+ @sasl_values.clear
104
+ end
105
+ else
106
+ break
107
+ end
108
+ end
109
+ end
110
+
111
+ def validate username, psw, sysname, realm
112
+ p username
113
+ p psw
114
+ p sysname
115
+ p realm
116
+ true
117
+ end
118
+ end
119
+
120
+ # Implements the SASL authd client protocol.
121
+ # This is a very, very simple protocol that mimics the one used
122
+ # by saslauthd and pwcheck, two outboard daemons included in the
123
+ # standard SASL library distro.
124
+ # The only thing this is really suitable for is SASL PLAIN
125
+ # (user+password) authentication, but the SASL libs that are
126
+ # linked into standard servers (like imapd and sendmail) implement
127
+ # the other ones.
128
+ #
129
+ # You can use this module directly as a handler for EM Connections,
130
+ # or include it in a module or handler class of your own.
131
+ #
132
+ # First connect to a SASL server (it's probably a TCP server, or more
133
+ # likely a Unix-domain socket). Then call the #validate? method,
134
+ # passing at least a username and a password. #validate? returns
135
+ # a Deferrable which will either succeed or fail, depending
136
+ # on the status of the authentication operation.
137
+ #
138
+ module SASLauthclient
139
+ MaxFieldSize = 128*1024
140
+
141
+ def validate? username, psw, sysname=nil, realm=nil
142
+
143
+ str = [username, psw, sysname, realm].map {|m|
144
+ [(m || "").length, (m || "")]
145
+ }.flatten.pack( "nA*" * 4 )
146
+ send_data str
147
+
148
+ d = EM::DefaultDeferrable.new
149
+ @queries.unshift d
150
+ d
151
+ end
152
+
153
+ def post_init
154
+ @sasl_data = ""
155
+ @queries = []
156
+ end
157
+
158
+ def receive_data data
159
+ @sasl_data << data
160
+
161
+ while @sasl_data.length > 2
162
+ len = (@sasl_data[0,2].unpack("n")).first
163
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
164
+ if @sasl_data.length >= (len + 2)
165
+ val = @sasl_data[2,len]
166
+ @sasl_data.slice!(0...(2+len))
167
+ q = @queries.pop
168
+ (val == "NO") ? q.fail : q.succeed
169
+ else
170
+ break
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ end
177
+ end
178
+
179
+
@@ -0,0 +1,308 @@
1
+ # $Id$
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+
27
+ #require 'base64'
28
+ require 'ostruct'
29
+
30
+ module EventMachine
31
+ module Protocols
32
+
33
+
34
+ class SmtpClient < Connection
35
+ include EventMachine::Deferrable
36
+ include EventMachine::Protocols::LineText2
37
+
38
+ # This is the external entry point.
39
+ #
40
+ # The argument is a hash containing these values:
41
+ # :host => a string containing the IP address or host name of the SMTP server to connect to.
42
+ # :port => optional, defaults to 25.
43
+ # :domain => required String. This is passed as the argument to the EHLO command.
44
+ # :starttls => optional. If it evaluates true, then the client will initiate STARTTLS with
45
+ # the server, and abort the connection if the negotiation doesn't succeed.
46
+ # TODO, need to be able to pass certificate parameters with this option.
47
+ # :auth => optional hash of auth parameters. If not given, then no auth will be attempted.
48
+ # (In that case, the connection will be aborted if the server requires auth.)
49
+ # Specify the hash value :type to determine the auth type, along with additional parameters
50
+ # depending on the type.
51
+ # Currently only :type => :plain is supported. Pass additional parameters :username (String),
52
+ # and :password (either a String or a Proc that will be called at auth-time).
53
+ # Example: :auth => {:type=>:plain, :username=>"mickey@disney.com", :password=>"mouse"}
54
+ # :from => required String. Specifies the sender of the message. Will be passed as the argument
55
+ # to the MAIL FROM. Do NOT enclose the argument in angle-bracket (<>) characters.
56
+ # The connection will abort if the server rejects the value.
57
+ # :to => required String or Array of Strings. The recipient(s) of the message. Do NOT enclose
58
+ # any of the values in angle-brackets (<>) characters. It's NOT a fatal error if one or more
59
+ # recipients are rejected by the server. (Of course, if ALL of them are, the server will most
60
+ # likely trigger an error when we try to send data.) An array of codes containing the status
61
+ # of each requested recipient is available after the call completes. TODO, we should define
62
+ # an overridable stub that will be called on rejection of a recipient or a sender, giving
63
+ # user code the chance to try again or abort the connection.
64
+ # :header => Required hash of values to be transmitted in the header of the message. The hash
65
+ # keys are the names of the headers (do NOT append a trailing colon), and the values are strings
66
+ # containing the header values. TODO, support Arrays of header values, which would cause us to
67
+ # send that specific header line more than once.
68
+ # Example: :header => {"Subject" => "Bogus", "CC" => "myboss@example.com"}
69
+ # :body => Optional string, defaults blank. This will be passed as the body of the email message.
70
+ # TODO, this needs to be significantly beefed up. As currently written, this requires the caller
71
+ # to properly format the input into CRLF-delimited lines of 7-bit characters in the standard
72
+ # SMTP transmission format. We need to be able to automatically convert binary data, and add
73
+ # correct line-breaks to text data. I think the :body parameter should remain as it is, and we
74
+ # should add a :content parameter that contains autoconversions and/or conversion parameters.
75
+ # Then we can check if either :body or :content is present and do the right thing.
76
+ # :verbose => Optional. If true, will cause a lot of information (including the server-side of the
77
+ # conversation) to be dumped to $>.
78
+ #
79
+ def self.send args={}
80
+ args[:port] ||= 25
81
+ args[:body] ||= ""
82
+
83
+ =begin
84
+ (I don't think it's possible for EM#connect to throw an exception under normal
85
+ circumstances, so this original code is stubbed out. A connect-failure will result
86
+ in the #unbind method being called without calling #connection_completed.)
87
+ begin
88
+ EventMachine.connect( args[:host], args[:port], self) {|c|
89
+ # According to the EM docs, we will get here AFTER post_init is called.
90
+ c.args = args
91
+ c.set_comm_inactivity_timeout 60
92
+ }
93
+ rescue
94
+ # We'll get here on a connect error. This code mimics the effect
95
+ # of a call to invoke_internal_error. Would be great to DRY this up.
96
+ # (Actually, it may be that we never get here, if EM#connect catches
97
+ # its errors internally.)
98
+ d = EM::DefaultDeferrable.new
99
+ d.set_deferred_status(:failed, {:error=>[:connect, 500, "unable to connect to server"]})
100
+ d
101
+ end
102
+ =end
103
+ EventMachine.connect( args[:host], args[:port], self) {|c|
104
+ # According to the EM docs, we will get here AFTER post_init is called.
105
+ c.args = args
106
+ c.set_comm_inactivity_timeout 60
107
+ }
108
+
109
+ end
110
+
111
+ attr_writer :args
112
+
113
+ def post_init
114
+ @return_values = OpenStruct.new
115
+ @return_values.start_time = Time.now
116
+ end
117
+
118
+ def connection_completed
119
+ @responder = :receive_signon
120
+ @msg = []
121
+ end
122
+
123
+ # We can get here in a variety of ways, all of them being failures unless
124
+ # the @succeeded flag is set. If a protocol success was recorded, then don't
125
+ # set a deferred success because the caller will already have done it
126
+ # (no need to wait until the connection closes to invoke the callbacks).
127
+ #
128
+ def unbind
129
+ unless @succeeded
130
+ @return_values.elapsed_time = Time.now - @return_values.start_time
131
+ @return_values.responder = @responder
132
+ @return_values.code = @code
133
+ @return_values.message = @msg
134
+ set_deferred_status(:failed, @return_values)
135
+ end
136
+ end
137
+
138
+ def receive_line ln
139
+ $>.puts ln if @args[:verbose]
140
+ @range = ln[0...1].to_i
141
+ @code = ln[0...3].to_i
142
+ @msg << ln[4..-1]
143
+ unless ln[3...4] == '-'
144
+ $>.puts @responder if @args[:verbose]
145
+ send @responder
146
+ @msg.clear
147
+ end
148
+ end
149
+
150
+ # We encountered an error from the server and will close the connection.
151
+ # Use the error and message the server returned.
152
+ #
153
+ def invoke_error
154
+ @return_values.elapsed_time = Time.now - @return_values.start_time
155
+ @return_values.responder = @responder
156
+ @return_values.code = @code
157
+ @return_values.message = @msg
158
+ set_deferred_status :failed, @return_values
159
+ send_data "QUIT\r\n"
160
+ close_connection_after_writing
161
+ end
162
+
163
+ # We encountered an error on our side of the protocol and will close the connection.
164
+ # Use an extra-protocol error code (900) and use the message from the caller.
165
+ #
166
+ def invoke_internal_error msg = "???"
167
+ @return_values.elapsed_time = Time.now - @return_values.start_time
168
+ @return_values.responder = @responder
169
+ @return_values.code = 900
170
+ @return_values.message = msg
171
+ set_deferred_status :failed, @return_values
172
+ send_data "QUIT\r\n"
173
+ close_connection_after_writing
174
+ end
175
+
176
+ def receive_signon
177
+ return invoke_error unless @range == 2
178
+ send_data "EHLO #{@args[:domain]}\r\n"
179
+ @responder = :receive_ehlo_response
180
+ end
181
+
182
+ def receive_ehlo_response
183
+ return invoke_error unless @range == 2
184
+ @server_caps = @msg
185
+ invoke_starttls
186
+ end
187
+
188
+ def invoke_starttls
189
+ if @args[:starttls]
190
+ # It would be more sociable to first ask if @server_caps contains
191
+ # the string "STARTTLS" before we invoke it, but hey, life's too short.
192
+ send_data "STARTTLS\r\n"
193
+ @responder = :receive_starttls_response
194
+ else
195
+ invoke_auth
196
+ end
197
+ end
198
+ def receive_starttls_response
199
+ return invoke_error unless @range == 2
200
+ start_tls
201
+ invoke_auth
202
+ end
203
+
204
+
205
+
206
+ # Perform an authentication. If the caller didn't request one, then fall through
207
+ # to the mail-from state.
208
+ def invoke_auth
209
+ if @args[:auth]
210
+ if @args[:auth][:type] == :plain
211
+ psw = @args[:auth][:password]
212
+ if psw.respond_to?(:call)
213
+ psw = psw.call
214
+ end
215
+ #str = Base64::encode64("\0#{@args[:auth][:username]}\0#{psw}").chomp
216
+ str = ["\0#{@args[:auth][:username]}\0#{psw}"].pack("m").chomp
217
+ send_data "AUTH PLAIN #{str}\r\n"
218
+ @responder = :receive_auth_response
219
+ else
220
+ return invoke_internal_error("unsupported auth type")
221
+ end
222
+ else
223
+ invoke_mail_from
224
+ end
225
+ end
226
+ def receive_auth_response
227
+ return invoke_error unless @range == 2
228
+ invoke_mail_from
229
+ end
230
+
231
+ def invoke_mail_from
232
+ send_data "MAIL FROM: <#{@args[:from]}>\r\n"
233
+ @responder = :receive_mail_from_response
234
+ end
235
+ def receive_mail_from_response
236
+ return invoke_error unless @range == 2
237
+ invoke_rcpt_to
238
+ end
239
+
240
+ def invoke_rcpt_to
241
+ @rcpt_responses ||= []
242
+ l = @rcpt_responses.length
243
+ to = @args[:to].is_a?(Array) ? @args[:to] : [@args[:to].to_s]
244
+ if l < to.length
245
+ send_data "RCPT TO: <#{to[l]}>\r\n"
246
+ @responder = :receive_rcpt_to_response
247
+ else
248
+ e = @rcpt_responses.select {|rr| rr.last == 2}
249
+ if e and e.length > 0
250
+ invoke_data
251
+ else
252
+ invoke_error
253
+ end
254
+ end
255
+ end
256
+ def receive_rcpt_to_response
257
+ @rcpt_responses << [@code, @msg, @range]
258
+ invoke_rcpt_to
259
+ end
260
+
261
+ def invoke_data
262
+ send_data "DATA\r\n"
263
+ @responder = :receive_data_response
264
+ end
265
+ def receive_data_response
266
+ return invoke_error unless @range == 3
267
+
268
+ # The data to send can be given either in @args[:content] (an array or string of raw data
269
+ # which MUST be in correct SMTP body format, including a trailing dot line), or a header and
270
+ # body given in @args[:header] and @args[:body].
271
+ #
272
+ if @args[:content]
273
+ send_data @args[:content].to_s
274
+ else
275
+ # The header can be a hash or an array.
276
+ if @args[:header].is_a?(Hash)
277
+ (@args[:header] || {}).each {|k,v| send_data "#{k}: #{v}\r\n" }
278
+ else
279
+ send_data @args[:header].to_s
280
+ end
281
+ send_data "\r\n"
282
+
283
+ if @args[:body].is_a?(Array)
284
+ @args[:body].each {|e| send_data e}
285
+ else
286
+ send_data @args[:body].to_s
287
+ end
288
+
289
+ send_data "\r\n.\r\n"
290
+ end
291
+
292
+ @responder = :receive_message_response
293
+ end
294
+ def receive_message_response
295
+ return invoke_error unless @range == 2
296
+ send_data "QUIT\r\n"
297
+ close_connection_after_writing
298
+ @succeeded = true
299
+ @return_values.elapsed_time = Time.now - @return_values.start_time
300
+ @return_values.responder = @responder
301
+ @return_values.code = @code
302
+ @return_values.message = @msg
303
+ set_deferred_status :succeeded, @return_values
304
+ end
305
+ end
306
+ end
307
+ end
308
+