Class: Percolate::Responder

Inherits:
Object
  • Object
show all
Defined in:
lib/percolate/responder.rb

Overview

This is the bit that actually handles the SMTP conversation with the client. Basically, you send it commands, and it acts on them. There is a small amount of Rubyish magic but that’s there mainly because I’m lazy. And besides, if I weren’t lazy, some guys on IRC would flame me. Haha! I kid. Greetz to RubyPanther by the wya. Even though I know you’ve never contributed anything and never will, at least you can live with the satisfaction that you don’t have to deal with my evil racist ass any more.

Instance Method Summary collapse

Constructor Details

#initialize(mailhostname, opts = {}) ⇒ Responder

Sets up the new smtp responder, with the parameter “mailhostname” as the SMTP hostname (as returned in the response to the SMTP HELO command.) Note that “mailhostname” can be anything at all, because I no longer believe that it’s possible to actually figure out your own full hostname without actually literally being told it. This is probably excessively-cynical of me, but I’ve seen what happens when wide-eyed optimists try to guess hostnames, and it just isn’t pretty.

Let’s just leave it at “mailhostname is a required parameter”, shall we?

Also, there are some interesting options you can give this. Well, only a couple. The first is :debug which you can set to true or false, which will cause it to print the SMTP conversation out. This is, of course, mostly only useful for debugging.

The other option, :originating_ip, probably more interesting, comes from the listener–the IP address of the client that connected.



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/percolate/responder.rb', line 63

def initialize(mailhostname, opts={})
    @verbose_debug = opts[:debug]
    @originating_ip = opts[:originating_ip]

    @mailhostname = mailhostname
    @current_state = nil
    @current_command = nil
    @response = connect
    @mail_object=nil
    @debug_output = []
    debug "\n\n"
end

Instance Method Details

#command(offered_command) ⇒ Object

Send an SMTP command to the responder. Use this to send a line of input to the responder (including a single line of DATA).

Parameters:

offered_command

The SMTP command (with end-of-line characters removed)



152
153
154
155
156
157
158
159
# File 'lib/percolate/responder.rb', line 152

def command offered_command
    debug ">>> #{offered_command}"
    begin
        dispatch offered_command
    rescue ResponderError => error
        @response = error.message
    end
end

#process_message(message_object) ⇒ Object

This is one of the methods you have to override in a subclass in order to use this class properly (unless chuckmail really is acceptable for you, in which case excellent! Also its default behaviour is to pretend to be an open relay, which should delight spammers until they figure out that all of their mail is being silently and cheerfully discarded, but they’re spammers so they won’t).

Parameters:

message_object

A SMTP::MessageObject object, with envelope data and the message itself (which could, as the RFC says, be any old crap at all! Don’t even expect an RFC2822-formatted message)



89
90
91
# File 'lib/percolate/responder.rb', line 89

def process_message message_object
    return "250 accepted, SMTP id is #{@mail_object.smtp_id}"
end

#responseObject

Returns the response from the server. When there’s no response, returns nil.

I still haven’t figured out what to do when there’s more than one response (like in the case of an ESMTP capabilities list).



138
139
140
141
142
143
# File 'lib/percolate/responder.rb', line 138

def response
    resp = @response
    @response = nil
    debug "<<< #{resp}"
    resp
end

#senderObject

The current message’s sender, if the MAIL FROM: command has been processed yet. If it hasn’t, then it returns nil, and probably isn’t meaningful anyway.



126
127
128
129
130
# File 'lib/percolate/responder.rb', line 126

def sender
    if @mail_object
        @mail_object.envelope_from
    end
end

#validate_recipient(address) ⇒ Object

Override this if you care about the recipient (which you should). When you get to this point, the accessor “sender” will work to return the sender, so that you can deal with both recipient and sender here.



119
120
121
# File 'lib/percolate/responder.rb', line 119

def validate_recipient address
    return true, "ok"
end

#validate_sender(address) ⇒ Object

Override this if you care about who the sender is (you probably do care who the sender is).

Incidentally, you probably Do Not Want To Become An Open Spam Relay–you really should validate both senders and recipients, and only accept mail if:

(a) the sender is local, and the recipient is remote, or (b) the sender is remote, and the recipient is local.

The definition of “local” and “remote” are, of course, up to you–if you’re using this to handle mail for a hundred domains, then all those hundred domains are local for you–but the idea is that you shoud be picky about who your mail is from and to.

This method takes one argument:

address

The email address you’re validating



111
112
113
# File 'lib/percolate/responder.rb', line 111

def validate_sender address
    return true, "ok"
end