# Twisted, the Framework of Your Internet
# Copyright (C) 2001 Matthew W. Lefkowitz
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""I am the support module for creating mail servers with 'mktap'
"""
import os
import sys
from twisted.mail import mail
from twisted.mail import maildir
from twisted.mail import relay
from twisted.mail import relaymanager
from twisted.mail import alias
from twisted.python import usage
from twisted.python import components
from twisted.cred import checkers
from twisted.application import internet
class Options(usage.Options):
synopsis = "Usage: mktap mail [options]"
optParameters = [
["pop3", "p", 8110, "Port to start the POP3 server on (0 to disable)."],
["pop3s", "S", 0, "Port to start the POP3-over-SSL server on (0 to disable)."],
["smtp", "s", 8025, "Port to start the SMTP server on (0 to disable)."],
["certificate", "c", None, "Certificate file to use for SSL connections"],
["relay", "R", None,
"Relay messages according to their envelope 'To', using the given"
"path as a queue directory."],
["hostname", "H", None, "The hostname by which to identify this server."],
]
optFlags = [
["esmtp", "E", "Use RFC 1425/1869 SMTP extensions"],
["disable-anonymous", None, "Disallow non-authenticated SMTP connections"],
]
longdesc = "This creates a mail.tap file that can be used by twistd."
def __init__(self):
usage.Options.__init__(self)
self.service = mail.MailService()
self.last_domain = None
def opt_passwordfile(self, filename):
"""Specify a file containing username:password login info for authenticated ESMTP connections."""
ch = checkers.OnDiskUsernamePasswordDatabase(filename)
self.service.smtpPortal.registerChecker(ch)
opt_P = opt_passwordfile
def opt_default(self):
"""Make the most recently specified domain the default domain."""
if self.last_domain:
self.service.addDomain('', self.last_domain)
else:
raise usage.UsageError("Specify a domain before specifying using --default")
opt_D = opt_default
def opt_maildirdbmdomain(self, domain):
"""generate an SMTP/POP3 virtual domain which saves to \"path\"
"""
try:
name, path = domain.split('=')
except ValueError:
raise usage.UsageError("Argument to --maildirdbmdomain must be of the form 'name=path'")
self.last_domain = maildir.MaildirDirdbmDomain(self.service, os.path.abspath(path))
self.service.addDomain(name, self.last_domain)
opt_d = opt_maildirdbmdomain
def opt_user(self, user_pass):
"""add a user/password to the last specified domains
"""
try:
user, password = user_pass.split('=', 1)
except ValueError:
raise usage.UsageError("Argument to --user must be of the form 'user=password'")
if self.last_domain:
self.last_domain.addUser(user, password)
else:
raise usage.UsageError("Specify a domain before specifying users")
opt_u = opt_user
def opt_bounce_to_postmaster(self):
"""undelivered mails are sent to the postmaster
"""
self.last_domain.postmaster = 1
opt_b = opt_bounce_to_postmaster
def opt_aliases(self, filename):
"""Specify an aliases(5) file to use for this domain"""
if self.last_domain:
if components.implements(self.last_domain, mail.IAliasableDomain):
aliases = alias.loadAliasFile(self.service.domains, filename)
self.last_domain.setAliasGroup(aliases)
self.service.monitor.monitorFile(
filename,
AliasUpdater(self.service.domains, self.last_domain)
)
else:
raise usage.UsageError(
"%s does not support alias files" % (
self.last_domain.__class__.__name__,
)
)
else:
raise usage.UsageError("Specify a domain before specifying aliases")
opt_A = opt_aliases
def postOptions(self):
for f in ('pop3', 'smtp', 'pop3s'):
try:
self[f] = int(self[f])
if not (0 <= self[f] < 2 ** 16):
raise ValueError
except ValueError:
raise usage.UsageError(
'Invalid port specified to --%s: %s' % (f, self[f])
)
if self['pop3s']:
if not self['certificate']:
raise usage.UsageError("Cannot specify --pop3s without "
"--certificate")
elif not os.path.exists(self['certificate']):
raise usage.UsageError("Certificate file %r does not exist."
% self['certificate'])
if not self['disable-anonymous']:
self.service.smtpPortal.registerChecker(checkers.AllowAnonymousAccess())
if not (self['pop3'] or self['smtp'] or self['pop3s']):
raise usage.UsageError("You cannot disable all protocols")
class AliasUpdater:
def __init__(self, domains, domain):
self.domains = domains
self.domain = domain
def __call__(self, new):
self.domain.setAliasGroup(alias.loadAliasFile(self.domains, new))
def makeService(config):
if config['esmtp']:
rmType = relaymanager.SmartHostESMTPRelayingManager
smtpFactory = config.service.getESMTPFactory
else:
rmType = relaymanager.SmartHostSMTPRelayingManager
smtpFactory = config.service.getSMTPFactory
if config['relay']:
dir = config['relay']
if not os.path.isdir(dir):
os.mkdir(dir)
config.service.setQueue(relaymanager.Queue(dir))
default = relay.DomainQueuer(config.service)
manager = rmType(config.service.queue)
if config['esmtp']:
manager.fArgs += (None, None)
manager.fArgs += (config['hostname'],)
helper = relaymanager.RelayStateHelper(manager, 1)
helper.setServiceParent(config.service)
config.service.domains.setDefaultDomain(default)
ctx = None
if config['certificate']:
from twisted.mail.protocols import SSLContextFactory
ctx = SSLContextFactory(config['certificate'])
if config['pop3']:
s = internet.TCPServer(config['pop3'], config.service.getPOP3Factory())
s.setServiceParent(config.service)
if config['pop3s']:
s = internet.SSLServer(config['pop3s'],
config.service.getPOP3Factory(), ctx)
s.setServiceParent(config.service)
if config['smtp']:
f = smtpFactory()
f.context = ctx
if config['hostname']:
f.domain = config['hostname']
f.fArgs = (f.domain,)
if config['esmtp']:
f.fArgs = (None, None) + f.fArgs
s = internet.TCPServer(config['smtp'], f)
s.setServiceParent(config.service)
return config.service
syntax highlighted by Code2HTML, v. 0.9.1