bridgedb.email

Servers for BridgeDB’s email bridge distributor.

bridgedb.email.autoresponder

Functionality for autoresponding to incoming emails.

Inheritance diagram of EmailResponse, SMTPAutoresponder

bridgedb.email.autoresponder
 | |_ createResponseBody - Parse lines from an incoming email and determine
 | |                       how to respond.
 | |_ generateResponse - Create an email response.
 |
 |_ EmailResponse - Holds information for generating a response to a request.
 |_ SMTPAutoresponder - An SMTP autoresponder for incoming mail.
createResponseBody(lines, context, client, lang=u'en')[source]

Parse the lines from an incoming email request and determine how to respond.

Parameters:
  • lines (list) – The list of lines from the original request sent by the client.
  • context (bridgedb.email.server.MailServerContext) – The context which contains settings for the email server.
  • client (twisted.mail.smtp.Address) – The client’s email address which should be in the 'To:' header of the response email.
  • lang (str) – The 2-5 character locale code to use for translating the email. This is obtained from a client sending a email to a valid plus address which includes the translation desired, i.e. by sending an email to bridges+fa@torproject.org, the client should receive a response in Farsi.
Return type:

str

Returns:

None if we shouldn’t respond to the client (i.e., if they have already received a rate-limiting warning email). Otherwise, returns a string containing the (optionally translated) body for the email response which we should send out.

generateResponse(fromAddress, client, body, subject=None, messageID=None, gpgSignFunc=None)[source]

Create an EmailResponse, which acts like an io.StringIO instance, by creating and writing all headers and the email body into the file-like EmailResponse.mailfile.

Parameters:
  • fromAddress (str) – The RFC 2821 email address which should be in the 'From:' header.
  • client (twisted.mail.smtp.Address) – The client’s email address which should be in the 'To:' header of the response email.
  • subject (str) – The string to write to the 'Subject:' header.
  • body (str) – The body of the email. If a gpgSignFunc is also given, then EmailResponse.writeBody() will generate and include an ascii-armored OpenPGP signature in the body.
  • messageID (None or str) – The RFC 2822 specifier for the 'Message-ID:' header, if including one is desirable.
  • gpgSignFunc (callable) – If given, this should be a callable function for signing messages. See bridgedb.crypto.initializeGnuPG() for obtaining a pre-configured gpgSignFunc.
Returns:

An EmailResponse which contains the entire email. To obtain the contents of the email, including all headers, simply use EmailResponse.readContents().

class EmailResponse(gpgSignFunc=None)[source]

Bases: object

Holds information for generating a response email for a request.

Todo

At some point, we may want to change this class to optionally handle creating Multipart MIME encoding messages, so that we can include attachments. (This would be useful for attaching our GnuPG keyfile, for example, rather than simply pasting it into the body of the email.)

Variables:
  • _buff (unicode or buffer) – Used internally to write lines for the response email into the _mailfile. The reason why both of these attributes have two possible types is for the same Python-buggy reasons which require NEW_BUFFER_INTERFACE.
  • mailfile (io.StringIO or io.BytesIO) – An in-memory file-like object for storing the formatted headers and body of the response email.
  • delimiter (str) – Delimiter between lines written to the mailfile.
  • closed (bool) – True if close() has been called.
  • to (twisted.mail.smtp.Address) – The client’s email address, to which this response should be sent.

Create a response to an email we have recieved.

This class deals with correctly formatting text for the response email headers and the response body into an instance of mailfile.

Parameters:gpgSignFunc (callable) – If given, this should be a callable function for signing messages. See bridgedb.crypto.initializeGnuPG() for obtaining a pre-configured gpgSignFunc.
mailfile

alias of BytesIO

close()[source]

Close our mailfile and set closed to True.

read(size=None)[source]

Read, at most, size bytes from our mailfile.

Note

This method is required by Twisted’s SMTP system.

Parameters:size (int) – The number of bytes to read. Defaults to None, which reads until EOF.
Return type:str
Returns:The bytes read from the mailfile.
readContents()[source]

Read the all the contents written thus far to the mailfile, and then seek() to return to the original pointer position we were at before this method was called.

Return type:str
Returns:The entire contents of the mailfile.
rewind()[source]

Rewind to the very beginning of the mailfile.

write(line)[source]

Write the line to the mailfile.

Any line written to me will have delimiter appended to it beforehand.

Parameters:line (str) – Something to append into the mailfile.
writelines(lines)[source]

Calls write() for each line in lines.

Line endings of '\r\n' will be replaced with delimiter (i.e. '\n'). See twisted.mail.smtp.SMTPClient.getMailData for the reason.

Parameters:lines (basestring or list) – The lines to write to the mailfile.
writeHeaders(fromAddress, toAddress, subject=None, inReplyTo=None, includeMessageID=True, contentType=u'text/plain; charset="utf-8"', **kwargs)[source]

Write all headers into the response email.

Parameters:
  • fromAddress (str) – The email address for the 'From:' header.
  • toAddress (str) – The email address for the 'To:' header.
  • subject (None or str) – The 'Subject:' header.
  • inReplyTo (None or str) – If set, an 'In-Reply-To:' header will be generated. This should be set to the 'Message-ID:' header from the client’s original request email.
  • includeMessageID (bool) – If True, generate and include a 'Message-ID:' header for the response.
  • contentType (str) – The 'Content-Type:' header.
Kwargs:

If given, the key will become the name of the header, and the value will become the contents of that header.

writeBody(body)[source]

Write the response body into the mailfile.

If EmailResponse.gpgSignFunc is set, and signing is configured, the body will be automatically signed before writing its contents into the mailfile.

Parameters:body (str) – The body of the response email.
class SMTPAutoresponder[source]

Bases: twisted.mail.smtp.SMTPClient

An twisted.mail.smtp.SMTPClient for responding to incoming mail.

The main worker in this class is the reply() method, which functions to dissect an incoming email from an incoming SMTPMessage and create a EmailResponse email message in reply to it, and then, finally, send it out.

Variables:

Handle responding (or not) to an incoming email.

debug = True
identity = 'wintermute'
getMailData()[source]

Gather all the data for building the response to the client.

This method must return a file-like object containing the data of the message to be sent. Lines in the file should be delimited by \n.

Return type:None or EmailResponse
Returns:An EmailResponse, if we have a response to send in reply to the incoming email, otherwise, returns None.
getMailTo()[source]

Attempt to get the client’s email address from an incoming email.

Return type:list
Returns:A list containing the client’s normalized email twisted.mail.smtp.Address, if it originated from a domain that we accept and the address was well-formed. Otherwise, returns None. Even though we’re likely to respond to only one client at a time, the return value of this method must be a list in order to hook into the rest of twisted.mail.smtp.SMTPClient correctly.
getMailFrom()[source]

Find our address in the recipients list of the incoming message.

Return type:str
Returns:Our address from the recipients list. If we can’t find it return our default EMAIL_FROM_ADDRESS from the config file.
sentMail(success)[source]

Callback for a twisted.mail.smtp.SMTPSenderFactory, called when an attempt to send an email is completed.

If some addresses were accepted, code and resp are the response to the DATA command. If no addresses were accepted, code is -1 and resp is an informative message.

Parameters:
  • code (int) – The code returned by the SMTP Server.
  • resp (str) – The string response returned from the SMTP Server.
  • numOK (int) – The number of addresses accepted by the remote host.
  • addresses (list) – A list of tuples (address, code, resp) listing the response to each RCPT TO command.
  • log – The SMTP session log. (We don’t use this, but it is sent by twisted.mail.smtp.SMTPSenderFactory nonetheless.)
sendError(fail)[source]

Errback for a twisted.mail.smtp.SMTPSenderFactory.

Parameters:fail (twisted.python.failure.Failure or twisted.mail.smtp.SMTPClientError) – An exception which occurred during the transaction to send the outgoing email.
reply()[source]

Reply to an incoming email. Maybe.

If nothing is returned from either createResponseBody() or generateResponse(), then the incoming email will not be responded to at all. This can happen for several reasons, for example: if the DKIM signature was invalid or missing, or if the incoming email came from an unacceptable domain, or if there have been too many emails from this client in the allotted time period.

Return type:twisted.internet.defer.Deferred
Returns:A Deferred which will callback when the response has been successfully sent, or errback if an error occurred while sending the email.
runChecks(client)[source]

Run checks on the incoming message, and only reply if they pass.

  1. Check if the client’s address is whitelisted.

2. If it’s not whitelisted, check that the domain names, taken from the SMTP MAIL FROM: command and the email 'From:' header, can be canonicalized.

  1. Check that those canonical domains match.

4. If the incoming message is from a domain which supports DKIM signing, then run bridgedb.email.dkim.checkDKIM() as well.

Note

Calling this method sets the incoming.canonicalFromEmail and incoming.canonicalDomainRules attributes of the incoming message.

Parameters:client (twisted.mail.smtp.Address) – The client’s email address, extracted from the 'From:' header from the incoming email.
Return type:bool
Returns:False if the checks didn’t pass, True otherwise.
send(response, retries=0, timeout=30, reaktor=<twisted.internet.epollreactor.EPollReactor object>)[source]

Send our response in reply to incoming.

Parameters:
  • client (twisted.mail.smtp.Address) – The email address of the client.
  • response – A EmailResponse.
  • retries (int) – Try resending this many times. (default: 0)
  • timeout (int) – Timeout after this many seconds. (default: 30)
Return type:

twisted.internet.defer.Deferred

Returns:

Our deferred.

bridgedb.email.autoresponder

A Distributor which hands out bridges to clients via an email interface.

Inheritance diagram of IgnoreEmail, TooSoonEmail, EmailRequestedHelp, EmailRequestedKey, EmailDistributor

MAX_EMAIL_RATE = 10800

The minimum amount of time (in seconds) which must pass before a client who has previously been given an email response must wait before being eligible to receive another response.

exception IgnoreEmail(msg, email)[source]

Bases: bridgedb.parse.addr.BadEmail

Raised when we get requests from this address after rate warning.

exception TooSoonEmail(msg, email)[source]

Bases: bridgedb.parse.addr.BadEmail

Raised when we got a request from this address too recently.

exception EmailRequestedHelp[source]

Bases: exceptions.Exception

Raised when a client has emailed requesting help.

exception EmailRequestedKey[source]

Bases: exceptions.Exception

Raised when an incoming email requested a copy of our GnuPG keys.

class EmailDistributor(key, domainmap, domainrules, answerParameters=None, whitelist=None)[source]

Bases: bridgedb.distribute.Distributor

Object that hands out bridges based on the email address of an incoming request and the current time period.

Variables:hashring – A hashring to hold all the bridges we hand out.

Create a bridge distributor which uses email.

Parameters:
  • emailHmac (callable) – An hmac function used to order email addresses within a ring. See getHMACFunc().
  • domainmap (dict) – A map from lowercase domains that we support mail from to their canonical forms. See EMAIL_DOMAIN_MAP option in bridgedb.conf.
  • domainrules – DOCDOC
  • answerParameters – DOCDOC
  • whitelist (dict or None) – A dictionary that maps whitelisted email addresses to GnuPG fingerprints.
emailRateMax = 10800

The minimum amount of time (in seconds) which must pass before a client who has previously been given an email response must wait before being eligible to receive another response.

bridgesPerResponse(hashring=None)[source]
getBridges(bridgeRequest, interval, clock=None)[source]

Return a list of bridges to give to a user.

Hint

All checks on the email address (which should be stored in the bridgeRequest.client attribute), such as checks for whitelisting and canonicalization of domain name, are done in bridgedb.email.autoresponder.getMailTo() and bridgedb.email.autoresponder.SMTPAutoresponder.runChecks().

Parameters:
  • bridgeRequest (EmailBridgeRequest) – A BridgeRequestBase with the client attribute set to a string containing the client’s full, canonicalized email address.
  • interval (str) – The time period when we got this request. This can be any string, so long as it changes with every period.
  • clock (twisted.internet.task.Clock) – If given, use the clock to ask what time it is, rather than time.time. This should likely only be used for testing.
Return type:

list or None

Returns:

A list of Bridges for the bridgeRequest.client, if allowed. Otherwise, returns None.

cleanDatabase()[source]

Clear all emailed response and warning times from the database.

prepopulateRings()[source]

Prepopulate this distributor’s hashrings and subhashrings with bridges.

bridgedb.email.dkim

Functions for checking DKIM verification results in email headers.

bridgedb.email.dkim
 |_ checkDKIM - Check the DKIM verification results header.
checkDKIM(message, rules)[source]

Check the DKIM verification results header.

This check is only run if the incoming email, message, originated from a domain for which we’re configured (in the EMAIL_DOMAIN_RULES dictionary in the config file) to check DKIM verification results for.

Returns False if:

  1. We’re supposed to expect and check the DKIM headers for the client’s email provider domain.
  2. Those headers were not okay.

Otherwise, returns True.

Parameters:
  • message (twisted.mail.smtp.rfc822.Message) – The incoming client request email, including headers.
  • rules (dict) – The list of configured EMAIL_DOMAIN_RULES for the canonical domain which the client’s email request originated from.
Return type:

bool

Returns:

False if the checks failed, True otherwise.

bridgedb.email.request

Classes for parsing and storing information about requests for bridges which are sent to the email distributor.

Inheritance diagram of EmailBridgeRequest

bridgedb.email.request
 | |_ determineBridgeRequestOptions - Figure out which filters to apply, or
 |                                    offer help.
 |_ EmailBridgeRequest - A request for bridges which was received through
                         the email distributor.
TRANSPORT_REGEXP = u'.*transport ([a-z][_a-z0-9]*)'

A regular expression for matching the Pluggable Transport method TYPE in emailed requests for Pluggable Transports.

UNBLOCKED_REGEXP = u'.*unblocked ([a-z]{2,4})'

A regular expression that matches country codes in requests for unblocked bridges.

determineBridgeRequestOptions(lines)[source]

Figure out which filters to apply, or offer help.

Note

If any 'transport TYPE' was requested, or bridges not blocked in a specific CC ('unblocked CC'), then the TYPE and/or CC will always be stored as a lowercase string.

Parameters:

lines (list) – A list of lines from an email, including the headers.

Raises:
  • EmailRequestedHelp – if the client requested help.
  • EmailRequestedKey – if the client requested our GnuPG key.
Return type:

EmailBridgeRequest

Returns:

A BridgeRequest with all of the requested parameters set. The returned BridgeRequest will have already had its filters generated via generateFilters().

class EmailBridgeRequest[source]

Bases: bridgedb.bridgerequest.BridgeRequestBase

We received a request for bridges through the email distributor.

Process a new bridge request received through the EmailDistributor.

wantsKey(wantsKey=None)[source]

Get or set whether this bridge request wanted our GnuPG key.

If called without parameters, this method will return the current state, otherwise (if called with the wantsKey parameter set), it will set the current state for whether or not this request wanted our key.

Parameters:wantsKey (bool) – If given, set the validity state of this request. Otherwise, get the current state.
withoutBlockInCountry(line)[source]

This request was for bridges not blocked in country.

Add any country code found in the line to the list of notBlockedIn. Currently, a request for a transport is recognized if the email line contains the 'unblocked' command.

Parameters:country (str) – The line from the email wherein the client requested some type of Pluggable Transport.
withPluggableTransportType(line)[source]

This request included a specific Pluggable Transport identifier.

Add any Pluggable Transport method TYPE found in the line to the list of transports. Currently, a request for a transport is recognized if the email line contains the 'transport' command.

Parameters:line (str) – The line from the email wherein the client requested some type of Pluggable Transport.

bridgedb.email.server

Servers which interface with clients and distribute bridges over SMTP.

Inheritance diagram of MailServerContext, SMTPMessage, SMTPIncomingDelivery, SMTPIncomingDeliveryFactory, SMTPIncomingServerFactory

bridgedb.email.server
 | |_ addServer - Set up a SMTP server which listens on the configured
 |                EMAIL_PORT for incoming connections, and responds as
 |                necessary to requests for bridges.
 |
 |_ MailServerContext - Helper object that holds information used by the
 |                      email subsystem.
 |_ SMTPMessage - Plugs into Twisted Mail and receives an incoming message.
 |_ SMTPIncomingDelivery - Plugs into SMTPIncomingServerFactory and handles
 |                         SMTP commands for incoming connections.
 |_ SMTPIncomingDeliveryFactory - Factory for SMTPIncomingDeliverys.
 |_ SMTPIncomingServerFactory - Plugs into twisted.mail.smtp.SMTPFactory;
                                creates a new SMTPMessageDelivery, which
                                handles response email automation, whenever
                                we get a incoming connection on the SMTP port.
class MailServerContext(config, distributor, schedule)[source]

Bases: object

Helper object that holds information used by email subsystem.

Variables:
  • username (str) – Reject any RCPT TO lines that aren’t to this user. See the EMAIL_USERNAME option in the config file. (default: 'bridges')
  • maximumSize (int) – Reject any incoming emails longer than this size (in bytes). (default: 3084 bytes).
  • smtpPort (int) – The port to use for outgoing SMTP.
  • smtpServer (str) – The IP address to use for outgoing SMTP.
  • smtpFromAddr (str) – Use this address in the raw SMTP MAIL FROM line for outgoing mail. (default: bridges@torproject.org)
  • fromAddr (str) – Use this address in the email From: line for outgoing mail. (default: bridges@torproject.org)
  • nBridges (int) – The number of bridges to send for each email.
  • blacklist (list) – A list of blacklisted email addresses, taken from the EMAIL_BLACKLIST config setting.
  • fuzzyMatch (int) – An integer specifying the maximum Levenshtein Distance from an incoming email address to a blacklisted email address for the incoming email to be dropped.
  • gpg – A gnupg.GPG interface, as returned by initialiseGnuPG(), or None if we couldn’t initialize GnuPG for some reason.
  • gpgSignFunc – A callable which signs a message, e.g. the one returned from initialiseGnuPG().

Create a context for storing configs for email bridge distribution.

Parameters:
  • distributor (EmailDistributor) – The distributor will handle getting the correct bridges (or none) for a client for us.
  • schedule (bridgedb.schedule.ScheduledInterval) – An interval-based scheduler, used to help the distributor know if we should give bridges to a client.
buildCanonicalDomainMap()[source]

Build a map for all email provider domains from which we will accept emails to their canonical domain name.

Note

Be sure that MailServerContext.domainRules and MailServerContext.domainMap are set appropriately before calling this method.

This method is automatically called during initialisation, and the resulting domain map is stored as MailServerContext.canon.

Return type:dict
Returns:A dictionary which maps all domains and subdomains which we accept emails from to their second-level, canonical domain names.
class SMTPMessage(context, canonicalFromSMTP=None)[source]

Bases: object

Plugs into the Twisted Mail and receives an incoming message.

Variables:
  • lines (list) – A list of lines from an incoming email message.
  • nBytes (int) – The number of bytes received thus far.
  • ignoring (bool) – If True, we’re ignoring the rest of this message because it exceeded MailServerContext.maximumSize.
  • canonicalFromSMTP – See runChecks().
  • canonicalFromEmail – See runChecks().
  • canonicalDomainRules – See runChecks().
  • message – (twisted.mail.smtp.rfc822.Message or None) The incoming email message.
  • responder – A SMTPAutoresponder which parses and checks the incoming message. If it decides to do so, it will build a reply() email and send() it.

Create a new SMTPMessage.

These are created automatically via SMTPIncomingDelivery.

Parameters:
  • context – The configured MailServerContext.
  • canonicalFromSMTP (str or None) – The canonical domain which this message was received from. For example, if 'gmail.com' is the configured canonical domain for 'googlemail.com' and a message is received from the latter domain, then this would be set to the former.
lineReceived(line)[source]

Called when we get another line of an incoming message.

eomReceived()[source]

Tell the responder to reply when we receive an EOM.

connectionLost()[source]

Called if we die partway through reading a message.

getIncomingMessage()[source]

Create and parse an RFC 2822 message object for all lines received thus far.

Return type:twisted.mail.smtp.rfc822.Message
Returns:A Message comprised of all lines received thus far.
class SMTPIncomingDelivery(delivery=None, deliveryFactory=None)[source]

Bases: twisted.mail.smtp.SMTP

Plugs into SMTPIncomingServerFactory and handles SMTP commands for incoming connections.

Variables:
  • context – A context containing SMTP/Email configuration settings.
  • deferred – A twisted.internet.defer.Deferred which will be returned when reply() is called. Additional callbacks may be set on this deferred in order to schedule additional actions when the response is being sent.
  • fromCanonicalSMTP – If set, this is the canonicalized domain name of the address we received from incoming connection’s MAIL FROM:.
context = None
deferred = <Deferred>
fromCanonicalSMTP = None
classmethod setContext(context)[source]

Set our context to a new MailServerContext.

receivedHeader(helo, origin, recipients)[source]

Create the Received: header for an incoming email.

Parameters:
validateFrom(helo, origin)[source]

Validate the MAIL FROM: address on the incoming SMTP connection.

This is done at the SMTP layer. Meaning that if a Postfix or other email server is proxying emails from the outside world to BridgeDB, the twisted.email.smtp.Address.domain will be set to the local hostname. Therefore, if the SMTP MAIL FROM: domain name is our own hostname (as returned from socket.gethostname()) or our own FQDN, allow the connection.

Otherwise, if the MAIL FROM: domain has a canonical domain in our mapping (taken from our context.canon, which is taken in turn from the EMAIL_DOMAIN_MAP), then our fromCanonicalSMTP is set to that domain.

Parameters:
  • helo (tuple) – The lines received during SMTP client HELO.
  • origin (twisted.mail.smtp.Address) – The email address we received this message from.
Raises:

twisted.mail.smtp.SMTPBadSender if the origin.domain was neither our local hostname, nor one of the canonical domains listed in context.canon.

Return type:

twisted.mail.smtp.Address

Returns:

The origin. We must return some non-None data from this method, or else Twisted will reply to the sender with a 503 error.

validateTo(user)[source]

Validate the SMTP RCPT TO: address for the incoming connection.

The local username and domain name to which this SMTP message is addressed, after being stripped of any '+' aliases, must be identical to those in the email address set our EMAIL_SMTP_FROM_ADDR configuration file option.

Parameters:user (twisted.mail.smtp.User) – Information about the user this SMTP message was addressed to.
Raises:A twisted.mail.smtp.SMTPBadRcpt if any of the above conditions weren’t met.
Return type:callable
Returns:A parameterless function which returns an instance of SMTPMessage.
class SMTPIncomingDeliveryFactory[source]

Bases: object

Factory for SMTPIncomingDelivery s.

This class is used to distinguish between different messages delivered over the same connection. This can be used to optimize delivery of a single message to multiple recipients, something which cannot be done by twisted.mail.smtp.IMessageDelivery implementors due to their lack of information.

Variables:
context = None
delivery

alias of SMTPIncomingDelivery

classmethod setContext(context)[source]

Set our context and the context for our delivery.

getMessageDelivery()[source]

Get a new SMTPIncomingDelivery instance.

class SMTPIncomingServerFactory(**kwargs)[source]

Bases: twisted.mail.smtp.SMTPFactory

Plugs into twisted.mail.smtp.SMTPFactory; creates a new SMTPIncomingDeliveryFactory, which handles response email automation whenever we get a incoming connection on the SMTP port.

Warning

My context isn’t an OpenSSL context, as is used for the twisted.mail.smtp.ESMTPSender.

Variables:
context = None
deliveryFactory

alias of SMTPIncomingDeliveryFactory

classmethod setContext(context)[source]

Set context and deliveryFactory.context.

buildProtocol(addr)[source]
addServer(config, distributor)[source]

Set up a SMTP server which listens on the configured EMAIL_PORT for incoming connections, and responds as necessary to requests for bridges.

Parameters:
  • config (bridgedb.configure.Conf) – A configuration object.
  • dist – A distributor which will handle database interactions, and will decide which bridges to give to who and when.

bridgedb.email.templates

Templates for formatting emails sent out by the email distributor.

addCommands(template)[source]

Add some text telling a client about supported email command, as well as which Pluggable Transports are currently available.

addGreeting(template, clientName=None, welcome=False)[source]
addKeyfile(template)[source]
addBridgeAnswer(template, answer)[source]
addHowto(template)[source]

Add help text on how to add bridges to Tor Browser.

Parameters:template (gettext.NullTranslation or gettext.GNUTranslation) – A gettext translations instance, optionally with fallback languages set.
addFooter(template, clientAddress=None)[source]

Add a footer:

 --
 <3 BridgeDB
________________________________________________________________________
Public Keys: https://bridges.torproject.org/keys

This email was generated with rainbows, unicorns, and sparkles
for alice@example.com on Friday, 09 May, 2014 at 18:59:39.
Parameters:
  • template (gettext.NullTranslation or gettext.GNUTranslation) – A gettext translations instance, optionally with fallback languages set.
  • clientAddress (twisted.mail.smtp.Address) – The client’s email address which should be in the To: header of the response email.
buildKeyMessage(template, clientAddress=None)[source]
buildWelcomeText(template, clientAddress=None)[source]
buildAnswerMessage(template, clientAddress=None, answer=None)[source]
buildSpamWarning(template, clientAddress=None)[source]