eventmachine 0.12.6-x86-mswin32-60 → 0.12.8-x86-mswin32-60

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