Class: MailDiode::Engine
- Inherits:
-
Object
- Object
- MailDiode::Engine
- Defined in:
- lib/engine.rb
Overview
State machine that handles incoming SMTP commands
Instance Method Summary collapse
- #add_filter(filter) ⇒ Object
- #apply_filters(sender, recipient) ⇒ Object
- #do_data(args) ⇒ Object
- #do_helo(args) ⇒ Object
- #do_mail(args) ⇒ Object
- #do_message_text(line) ⇒ Object
- #do_noop(args) ⇒ Object
- #do_quit(args) ⇒ Object
- #do_rcpt(args) ⇒ Object
- #do_rset(args) ⇒ Object
- #do_vrfy(args) ⇒ Object
- #enforce_address_arg(args, keyword, error_text) ⇒ Object
- #enforce_host_arg(args, error_text) ⇒ Object
- #enforce_no_args(args, error_text) ⇒ Object
- #extract_args(line) ⇒ Object
-
#initialize(hostname) ⇒ Engine
constructor
A new instance of Engine.
- #process_command(command, args) ⇒ Object
- #process_line(line) ⇒ Object
- #process_message(sender, recipient, text) ⇒ Object
- #raise_smtp_error(text) ⇒ Object
- #set_mail_handler(handler) ⇒ Object
- #start(sender_ip) ⇒ Object
- #terminate? ⇒ Boolean
- #terminate_message ⇒ Object
Constructor Details
#initialize(hostname) ⇒ Engine
Returns a new instance of Engine.
84 85 86 87 88 89 |
# File 'lib/engine.rb', line 84 def initialize(hostname) @hostname = hostname @greeting = "220 #{hostname} ESMTP" @helo_response = "250 #{hostname}" @filters = [] end |
Instance Method Details
#add_filter(filter) ⇒ Object
95 96 97 |
# File 'lib/engine.rb', line 95 def add_filter(filter) @filters << filter end |
#apply_filters(sender, recipient) ⇒ Object
219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/engine.rb', line 219 def apply_filters(sender, recipient) filterable_data = FilterableData.new filterable_data.sender_ip = @sender_ip filterable_data.helo = @helo filterable_data.sender = sender filterable_data.recipient = recipient @filters.each do | filter | if filter.respond_to? :process filter.process(filterable_data) end end return filterable_data end |
#do_data(args) ⇒ Object
206 207 208 209 210 211 212 213 |
# File 'lib/engine.rb', line 206 def do_data(args) if(! @envelope || ! @envelope.sender) raise_smtp_error(SMTPError::NEED_RCPT_BEFORE_DATA) end enforce_no_args(args, SMTPError::SYNTAX_DATA) @message_text = [] return RESULT_DATA_OK end |
#do_helo(args) ⇒ Object
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/engine.rb', line 152 def do_helo(args) enforce_host_arg(args, SMTPError::SYNTAX_HELO) @helo = args @filters.each do | filter | if filter.respond_to? :helo filter.helo @helo end end return @helo_response end |
#do_mail(args) ⇒ Object
174 175 176 177 178 179 180 181 182 183 |
# File 'lib/engine.rb', line 174 def do_mail(args) from_address = enforce_address_arg(args, 'from', SMTPError::SYNTAX_MAIL) @envelope = Envelope.new(from_address) @filters.each do | filter | if filter.respond_to? :mail filter.mail from_address end end return RESULT_OK end |
#do_message_text(line) ⇒ Object
233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/engine.rb', line 233 def (line) if line == '.' return end if line[0..0] == '.' line = line[1..-1] end @message_text << line return nil end |
#do_noop(args) ⇒ Object
141 142 143 144 |
# File 'lib/engine.rb', line 141 def do_noop(args) enforce_no_args(args, SMTPError::SYNTAX_NOOP) return RESULT_OK end |
#do_quit(args) ⇒ Object
146 147 148 149 150 |
# File 'lib/engine.rb', line 146 def do_quit(args) enforce_no_args(args, SMTPError::SYNTAX_QUIT) @should_terminate = true return RESULT_BYE end |
#do_rcpt(args) ⇒ Object
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/engine.rb', line 185 def do_rcpt(args) if(! @envelope) raise_smtp_error(SMTPError::NEED_MAIL_BEFORE_RCPT) end if(@envelope.recipients.size >= 100) raise_smtp_error(SMTPError::TOO_MANY_RECIPIENTS) end recipient = enforce_address_arg(args, 'to', SMTPError::SYNTAX_RCPT) @filters.each do | filter | if filter.respond_to? :rcpt filter.rcpt recipient end end filterable_data = apply_filters(@envelope.sender, recipient) if !@mail_handler.valid_recipient?(filterable_data.recipient) raise_smtp_error(SMTPError::UNKNOWN_RECIPIENT + filterable_data.recipient) end @envelope.add_recipient(recipient) return RESULT_OK end |
#do_rset(args) ⇒ Object
168 169 170 171 172 |
# File 'lib/engine.rb', line 168 def do_rset(args) enforce_no_args(args, SMTPError::SYNTAX_RSET) @envelope = nil return RESULT_OK end |
#do_vrfy(args) ⇒ Object
163 164 165 166 |
# File 'lib/engine.rb', line 163 def do_vrfy(args) enforce_host_arg(args, SMTPError::SYNTAX_VRFY) return RESULT_UNSURE end |
#enforce_address_arg(args, keyword, error_text) ⇒ Object
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/engine.rb', line 285 def enforce_address_arg(args, keyword, error_text) re = /#{keyword}:\s*(.+)/i match = re.match(args) if !match raise_smtp_error(error_text) end candidate = match[1].strip strip_brackets = /<(.*)>/ match = strip_brackets.match(candidate) if(match) candidate = match[1] end return candidate end |
#enforce_host_arg(args, error_text) ⇒ Object
278 279 280 281 282 283 |
# File 'lib/engine.rb', line 278 def enforce_host_arg(args, error_text) args = args.split(/\s+/) if args.size != 1 raise_smtp_error(error_text) end end |
#enforce_no_args(args, error_text) ⇒ Object
272 273 274 275 276 |
# File 'lib/engine.rb', line 272 def enforce_no_args(args, error_text) if ! args.empty? raise_smtp_error(error_text) end end |
#extract_args(line) ⇒ Object
301 302 303 304 305 306 307 308 309 310 |
# File 'lib/engine.rb', line 301 def extract_args(line) command, args = line.split(nil, 2) if command.nil? command = '' end if args.nil? args = '' end return command, args end |
#process_command(command, args) ⇒ Object
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/engine.rb', line 125 def process_command(command, args) case command.upcase when NOOP then return do_noop(args) when QUIT then return do_quit(args) when HELO then return do_helo(args) when EHLO then return do_helo(args) when VRFY then return do_vrfy(args) when RSET then return do_rset(args) when MAIL then return do_mail(args) when RCPT then return do_rcpt(args) when DATA then return do_data(args) end raise_smtp_error(SMTPError::BAD_COMMAND + ': ' + command) end |
#process_line(line) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/engine.rb', line 104 def process_line(line) begin if @message_text return (line) end command, args = extract_args(line) result = process_command(command, args) MailDiode::log_success(command, args, result) return result rescue SMTPError => error MailDiode.log_info(error.text) return error.text end end |
#process_message(sender, recipient, text) ⇒ Object
260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/engine.rb', line 260 def (sender, recipient, text) received = "Received: from #{@helo} (#{@sender_ip}) " + "by #{@hostname} " + "for <#{recipient}> " + "at #{Time.now.to_s}" full_text = received + NEWLINE + text.join(NEWLINE) filterable_data = apply_filters(sender, recipient) id = @mail_handler.(filterable_data.recipient, full_text) return id end |
#raise_smtp_error(text) ⇒ Object
312 313 314 |
# File 'lib/engine.rb', line 312 def raise_smtp_error(text) raise SMTPError.new(text) end |
#set_mail_handler(handler) ⇒ Object
91 92 93 |
# File 'lib/engine.rb', line 91 def set_mail_handler(handler) @mail_handler = handler end |
#start(sender_ip) ⇒ Object
99 100 101 102 |
# File 'lib/engine.rb', line 99 def start(sender_ip) @sender_ip = sender_ip return @greeting end |
#terminate? ⇒ Boolean
121 122 123 |
# File 'lib/engine.rb', line 121 def terminate? return @should_terminate end |
#terminate_message ⇒ Object
246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/engine.rb', line 246 def sender = @envelope.sender recipients = @envelope.recipients text = @message_text @envelope = nil @message_text = nil ids = [] recipients.each do | recipient | ids << (sender, recipient, text) end return "#{RESULT_OK} #{ids.join(';')}" end |