Class: Celluloid::SMTP::Connection
- Inherits:
-
Object
- Object
- Celluloid::SMTP::Connection
- Extended by:
- Forwardable
- Includes:
- Celluloid::SMTP, Extensions
- Defined in:
- lib/celluloid/smtp.rb,
lib/celluloid/smtp/connection.rb,
lib/celluloid/smtp/connection/events.rb,
lib/celluloid/smtp/connection/parser.rb
Defined Under Namespace
Classes: Automata
Constant Summary
Constants included from Celluloid::SMTP
Instance Attribute Summary collapse
-
#automata ⇒ Object
readonly
Returns the value of attribute automata.
-
#socket ⇒ Object
readonly
Returns the value of attribute socket.
Instance Method Summary collapse
- #context!(first = false) ⇒ Object
- #delivering? ⇒ Boolean
- #envelope ⇒ Object
- #event!(method, *args) ⇒ Object
- #finish! ⇒ Object
- #handle! ⇒ Object
-
#initialize(socket, configuration) ⇒ Connection
constructor
A new instance of Connection.
- #length ⇒ Object
- #line!(data) ⇒ Object
- #message ⇒ Object
- #on_connection ⇒ Object
- #on_disconnect ⇒ Object
- #on_helo(helo) ⇒ Object
- #on_mail_from(from) ⇒ Object
- #on_message(message) ⇒ Object
- #on_rcpt_to(to) ⇒ Object
- #print!(string) ⇒ Object
- #process! ⇒ Object
- #relaying? ⇒ Boolean
- #remote_host ⇒ Object
- #remote_ip ⇒ Object (also: #remote_addr)
- #start! ⇒ Object
Methods included from Extensions
Constructor Details
#initialize(socket, configuration) ⇒ Connection
Returns a new instance of Connection.
9 10 11 12 13 14 15 16 17 |
# File 'lib/celluloid/smtp/connection.rb', line 9 def initialize(socket, configuration) @automata = Automata.new(self) @configuration = configuration.dup @socket = socket = {} @context = nil @behavior = configuration.fetch(:behavior, DEFAULT_BEHAVIOR) transition :connection end |
Instance Attribute Details
#automata ⇒ Object (readonly)
Returns the value of attribute automata.
3 4 5 |
# File 'lib/celluloid/smtp/connection.rb', line 3 def automata @automata end |
#socket ⇒ Object (readonly)
Returns the value of attribute socket.
3 4 5 |
# File 'lib/celluloid/smtp/connection.rb', line 3 def socket @socket end |
Instance Method Details
#context!(first = false) ⇒ Object
22 23 24 25 26 27 28 |
# File 'lib/celluloid/smtp/connection/parser.rb', line 22 def context!(first=false) @helo = nil if first @sequence = first ? :helo : :rset @context = {} if first || @context.nil? @context[:envelope] = {from: "", to: []} @context[:message] = {delivered: -1, bytesize: -1, data: ""} end |
#delivering? ⇒ Boolean
36 37 38 |
# File 'lib/celluloid/smtp/connection.rb', line 36 def delivering? @behavior == :deliver end |
#envelope ⇒ Object
30 |
# File 'lib/celluloid/smtp/connection/parser.rb', line 30 def envelope; @context[:envelope] ||= {} end |
#event!(method, *args) ⇒ Object
3 4 5 6 7 8 9 10 11 12 |
# File 'lib/celluloid/smtp/connection/events.rb', line 3 def event!(method,*args) start = Time.now debug("Executing event: #{method}") if DEBUG_EVENTS result = send(method,*args) debug("TIMER: #{"%0.4f" %(Time.now-start)} on event: #{method}") if DEBUG_TIMING result rescue => ex exception(ex, "Failure in event processor: #{method}") nil end |
#finish! ⇒ Object
23 24 25 |
# File 'lib/celluloid/smtp/connection.rb', line 23 def finish! [:finish] = Time.now end |
#handle! ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 |
# File 'lib/celluloid/smtp/connection/parser.rb', line 5 def handle! context!(true) if process! transition :handled else transition :disconnecting end rescue => ex exception(ex, "Error handling command session") transition :closed end |
#length ⇒ Object
27 28 29 30 |
# File 'lib/celluloid/smtp/connection.rb', line 27 def length raise "Connection incomplete." unless [:start] && [:finish] [:finish].to_f - [:start].to_f end |
#line!(data) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/celluloid/smtp/connection/parser.rb', line 64 def line!(data) unless data? case data when (/^(HELO|EHLO)(\s+.*)?$/i) # HELO/EHLO # 250 Requested mail action okay, completed # 421 <domain> Service not available, closing transmission channel # 500 Syntax error, command unrecognised # 501 Syntax error in parameters or arguments # 504 Command parameter not implemented # 521 <domain> does not accept mail [rfc1846] raise Error503 unless helo? data = data.gsub(/^(HELO|EHLO)\ /i, '').strip if return_value = event!(:on_helo, data) data = return_value end @helo = data rset! return "250 OK" when (/^NOOP\s*$/i) # NOOP # 250 Requested mail action okay, completed # 421 <domain> Service not available, closing transmission channel # 500 Syntax error, command unrecognised return "250 OK" when (/^RSET\s*$/i) # RSET # 250 Requested mail action okay, completed # 421 <domain> Service not available, closing transmission channel # 500 Syntax error, command unrecognised # 501 Syntax error in parameters or arguments raise Error503 if helo? # handle command context! return "250 OK" when (/^QUIT\s*$/i) # QUIT # 221 <domain> Service closing transmission channel # 500 Syntax error, command unrecognised quit! return "" when (/^MAIL FROM\:/i) # MAIL # 250 Requested mail action okay, completed # 421 <domain> Service not available, closing transmission channel # 451 Requested action aborted: local error in processing # 452 Requested action not taken: insufficient system storage # 500 Syntax error, command unrecognised # 501 Syntax error in parameters or arguments # 552 Requested mail action aborted: exceeded storage allocation raise Error503 unless rset? data = data.gsub(/^MAIL FROM\:/i, '').strip if return_value = event!(:on_mail_from, data) data = return_value end envelope[:from] = data mail! return "250 OK" when (/^RCPT TO\:/i) # RCPT # 250 Requested mail action okay, completed # 251 User not local; will forward to <forward-path> # 421 <domain> Service not available, closing transmission channel # 450 Requested mail action not taken: mailbox unavailable # 451 Requested action aborted: local error in processing # 452 Requested action not taken: insufficient system storage # 500 Syntax error, command unrecognised # 501 Syntax error in parameters or arguments # 503 Bad sequence of commands # 521 <domain> does not accept mail [rfc1846] # 550 Requested action not taken: mailbox unavailable # 551 User not local; please try <forward-path> # 552 Requested mail action aborted: exceeded storage allocation # 553 Requested action not taken: mailbox name not allowed raise Error503 unless mail? || rset? data = data.gsub(/^RCPT TO\:/i, '').strip if return_value = event!(:on_rcpt_to, data) data = return_value end envelope[:to] << data rset! return "250 OK" when (/^DATA\s*$/i) # DATA # 354 Start mail input; end with <CRLF>.<CRLF> # 250 Requested mail action okay, completed # 421 <domain> Service not available, closing transmission channel received data # 451 Requested action aborted: local error in processing # 452 Requested action not taken: insufficient system storage # 500 Syntax error, command unrecognised # 501 Syntax error in parameters or arguments # 503 Bad sequence of commands # 552 Requested mail action aborted: exceeded storage allocation # 554 Transaction failed raise Error503 unless rset? data! return "354 Enter message, ending with \".\" on a data by itself" else raise Error500 end else # Data mode. if (data.chomp =~ /^\.$/) # Line with only a period; being told to exit data mode. [:data] += data [:data].gsub!(/\r\n\Z/, '').gsub!(/\.\Z/, '') # remove ending line . begin if return_value = event!(:on_message, @context) [:data] = return_value end [:delivered] = Time.now.utc # save delivered time [:bytesize] = [:data].bytesize # save bytesize of message data return "250 Requested mail action okay, completed" rescue Celluloid::SMTP::Exception raise rescue Exception => ex raise Error451.new("#{ex}") ensure context! end else [:data] += data return "" end end end |
#message ⇒ Object
31 |
# File 'lib/celluloid/smtp/connection/parser.rb', line 31 def ; @context[:message] ||= {} end |
#on_connection ⇒ Object
14 15 16 |
# File 'lib/celluloid/smtp/connection/events.rb', line 14 def on_connection debug("Client connected.") if DEBUG_EVENTS end |
#on_disconnect ⇒ Object
18 19 20 |
# File 'lib/celluloid/smtp/connection/events.rb', line 18 def on_disconnect debug("Disconnecting client.") if DEBUG_EVENTS end |
#on_helo(helo) ⇒ Object
22 23 24 25 |
# File 'lib/celluloid/smtp/connection/events.rb', line 22 def on_helo(helo) debug("HELO: #{helo}") if DEBUG_EVENTS return helo end |
#on_mail_from(from) ⇒ Object
27 28 29 30 |
# File 'lib/celluloid/smtp/connection/events.rb', line 27 def on_mail_from(from) debug("MAIL FROM: #{from}") if DEBUG_EVENTS return from end |
#on_message(message) ⇒ Object
37 38 39 40 |
# File 'lib/celluloid/smtp/connection/events.rb', line 37 def () debug("MESSAGE") if DEBUG_EVENTS return [:data] end |
#on_rcpt_to(to) ⇒ Object
32 33 34 35 |
# File 'lib/celluloid/smtp/connection/events.rb', line 32 def on_rcpt_to(to) debug("RCPT TO: #{to}") if DEBUG_EVENTS return to end |
#print!(string) ⇒ Object
40 41 42 |
# File 'lib/celluloid/smtp/connection.rb', line 40 def print!(string) print "#{string}\r\n" end |
#process! ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/celluloid/smtp/connection/parser.rb', line 33 def process! print! "220 #{@configuration[:hostname]} says welcome!" begin loop { output = begin data = @socket.readline debug(">> #{data.chomp}") unless data? if DEBUG line! data rescue Celluloid::SMTP::Exception => ex exception(ex, "Processing error") rescue => ex exception(ex, "Unknown exception") Error500.new.result end unless output.empty? debug("<< #{output}") if DEBUG print! output end break if quit? || closed? } print! "221 Service closing transmission channel" unless closed? return true rescue EOFError debug("Lost connection due to client abort.") rescue Exception => ex exception(ex, "Error parsing command session") print! Error421.new.result unless closed? end false end |
#relaying? ⇒ Boolean
32 33 34 |
# File 'lib/celluloid/smtp/connection.rb', line 32 def @behavior == :relay end |
#remote_host ⇒ Object
49 50 51 52 |
# File 'lib/celluloid/smtp/connection.rb', line 49 def remote_host # NOTE: This is currently a blocking operation. peeraddr(true)[2] end |
#remote_ip ⇒ Object Also known as: remote_addr
44 45 46 |
# File 'lib/celluloid/smtp/connection.rb', line 44 def remote_ip peeraddr(false)[3] end |
#start! ⇒ Object
19 20 21 |
# File 'lib/celluloid/smtp/connection.rb', line 19 def start! [:start] = Time.now end |