aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py3/twisted/mail/tap.py
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py3/twisted/mail/tap.py
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/mail/tap.py')
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/tap.py384
1 files changed, 384 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py3/twisted/mail/tap.py b/contrib/python/Twisted/py3/twisted/mail/tap.py
new file mode 100644
index 0000000000..1217808e3a
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/mail/tap.py
@@ -0,0 +1,384 @@
+# -*- test-case-name: twisted.mail.test.test_options -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+
+"""
+Support for creating mail servers with twistd.
+"""
+
+import os
+
+from twisted.application import internet
+from twisted.cred import checkers, strcred
+from twisted.internet import endpoints
+from twisted.mail import alias, mail, maildir, relay, relaymanager
+from twisted.python import usage
+
+
+class Options(usage.Options, strcred.AuthOptionMixin):
+ """
+ An options list parser for twistd mail.
+
+ @type synopsis: L{bytes}
+ @ivar synopsis: A description of options for use in the usage message.
+
+ @type optParameters: L{list} of L{list} of (0) L{bytes}, (1) L{bytes},
+ (2) L{object}, (3) L{bytes}, (4) L{None} or
+ callable which takes L{bytes} and returns L{object}
+ @ivar optParameters: Information about supported parameters. See
+ L{Options <twisted.python.usage.Options>} for details.
+
+ @type optFlags: L{list} of L{list} of (0) L{bytes}, (1) L{bytes} or
+ L{None}, (2) L{bytes}
+ @ivar optFlags: Information about supported flags. See
+ L{Options <twisted.python.usage.Options>} for details.
+
+ @type _protoDefaults: L{dict} mapping L{bytes} to L{int}
+ @ivar _protoDefaults: A mapping of default service to port.
+
+ @type compData: L{Completions <usage.Completions>}
+ @ivar compData: Metadata for the shell tab completion system.
+
+ @type longdesc: L{bytes}
+ @ivar longdesc: A long description of the plugin for use in the usage
+ message.
+
+ @type service: L{MailService}
+ @ivar service: The email service.
+
+ @type last_domain: L{IDomain} provider or L{None}
+ @ivar last_domain: The most recently specified domain.
+ """
+
+ synopsis = "[options]"
+
+ optParameters = [
+ [
+ "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"],
+ ["no-pop3", None, "Disable the default POP3 server."],
+ ["no-smtp", None, "Disable the default SMTP server."],
+ ]
+
+ _protoDefaults = {
+ "pop3": 8110,
+ "smtp": 8025,
+ }
+
+ compData = usage.Completions(optActions={"hostname": usage.CompleteHostnames()})
+
+ longdesc = """
+ An SMTP / POP3 email server plugin for twistd.
+
+ Examples:
+
+ 1. SMTP and POP server
+
+ twistd mail --maildirdbmdomain=example.com=/tmp/example.com
+ --user=joe=password
+
+ Starts an SMTP server that only accepts emails to joe@example.com and saves
+ them to /tmp/example.com.
+
+ Also starts a POP mail server which will allow a client to log in using
+ username: joe@example.com and password: password and collect any email that
+ has been saved in /tmp/example.com.
+
+ 2. SMTP relay
+
+ twistd mail --relay=/tmp/mail_queue
+
+ Starts an SMTP server that accepts emails to any email address and relays
+ them to an appropriate remote SMTP server. Queued emails will be
+ temporarily stored in /tmp/mail_queue.
+ """
+
+ def __init__(self):
+ """
+ Parse options and create a mail service.
+ """
+ usage.Options.__init__(self)
+ self.service = mail.MailService()
+ self.last_domain = None
+ for service in self._protoDefaults:
+ self[service] = []
+
+ def addEndpoint(self, service, description):
+ """
+ Add an endpoint to a service.
+
+ @type service: L{bytes}
+ @param service: A service, either C{b'smtp'} or C{b'pop3'}.
+
+ @type description: L{bytes}
+ @param description: An endpoint description string or a TCP port
+ number.
+ """
+ from twisted.internet import reactor
+
+ self[service].append(endpoints.serverFromString(reactor, description))
+
+ def opt_pop3(self, description):
+ """
+ Add a POP3 port listener on the specified endpoint.
+
+ You can listen on multiple ports by specifying multiple --pop3 options.
+ """
+ self.addEndpoint("pop3", description)
+
+ opt_p = opt_pop3
+
+ def opt_smtp(self, description):
+ """
+ Add an SMTP port listener on the specified endpoint.
+
+ You can listen on multiple ports by specifying multiple --smtp options.
+ """
+ self.addEndpoint("smtp", description)
+
+ opt_s = opt_smtp
+
+ 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.
+
+ This option requires an argument of the form 'NAME=PATH' where NAME is
+ the DNS domain name for which email will be accepted and where PATH is
+ a the filesystem path to a Maildir folder.
+ [Example: 'example.com=/tmp/example.com']
+ """
+ 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 and password to the last specified domain.
+ """
+ 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):
+ """
+ Send undeliverable messages 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 the last specified domain.
+ """
+ if self.last_domain is not None:
+ if mail.IAliasableDomain.providedBy(self.last_domain):
+ 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 _getEndpoints(self, reactor, service):
+ """
+ Return a list of endpoints for the specified service, constructing
+ defaults if necessary.
+
+ If no endpoints were configured for the service and the protocol
+ was not explicitly disabled with a I{--no-*} option, a default
+ endpoint for the service is created.
+
+ @type reactor: L{IReactorTCP <twisted.internet.interfaces.IReactorTCP>}
+ provider
+ @param reactor: If any endpoints are created, the reactor with
+ which they are created.
+
+ @type service: L{bytes}
+ @param service: The type of service for which to retrieve endpoints,
+ either C{b'pop3'} or C{b'smtp'}.
+
+ @rtype: L{list} of L{IStreamServerEndpoint
+ <twisted.internet.interfaces.IStreamServerEndpoint>} provider
+ @return: The endpoints for the specified service as configured by the
+ command line parameters.
+ """
+ if self[service]:
+ # If there are any services set up, just return those.
+ return self[service]
+ elif self["no-" + service]:
+ # If there are no services, but the service was explicitly disabled,
+ # return nothing.
+ return []
+ else:
+ # Otherwise, return the old default service.
+ return [endpoints.TCP4ServerEndpoint(reactor, self._protoDefaults[service])]
+
+ def postOptions(self):
+ """
+ Check the validity of the specified set of options and
+ configure authentication.
+
+ @raise UsageError: When the set of options is invalid.
+ """
+ from twisted.internet import reactor
+
+ if self["esmtp"] and self["hostname"] is None:
+ raise usage.UsageError("--esmtp requires --hostname")
+
+ # If the --auth option was passed, this will be present -- otherwise,
+ # it won't be, which is also a perfectly valid state.
+ if "credCheckers" in self:
+ for ch in self["credCheckers"]:
+ self.service.smtpPortal.registerChecker(ch)
+
+ if not self["disable-anonymous"]:
+ self.service.smtpPortal.registerChecker(checkers.AllowAnonymousAccess())
+
+ anything = False
+ for service in self._protoDefaults:
+ self[service] = self._getEndpoints(reactor, service)
+ if self[service]:
+ anything = True
+
+ if not anything:
+ raise usage.UsageError("You cannot disable all protocols")
+
+
+class AliasUpdater:
+ """
+ A callable object which updates the aliases for a domain from an aliases(5)
+ file.
+
+ @ivar domains: See L{__init__}.
+ @ivar domain: See L{__init__}.
+ """
+
+ def __init__(self, domains, domain):
+ """
+ @type domains: L{dict} mapping L{bytes} to L{IDomain} provider
+ @param domains: A mapping of domain name to domain object
+
+ @type domain: L{IAliasableDomain} provider
+ @param domain: The domain to update.
+ """
+ self.domains = domains
+ self.domain = domain
+
+ def __call__(self, new):
+ """
+ Update the aliases for a domain from an aliases(5) file.
+
+ @type new: L{bytes}
+ @param new: The name of an aliases(5) file.
+ """
+ self.domain.setAliasGroup(alias.loadAliasFile(self.domains, new))
+
+
+def makeService(config):
+ """
+ Configure a service for operating a mail server.
+
+ The returned service may include POP3 servers, SMTP servers, or both,
+ depending on the configuration passed in. If there are multiple servers,
+ they will share all of their non-network state (i.e. the same user accounts
+ are available on all of them).
+
+ @type config: L{Options <usage.Options>}
+ @param config: Configuration options specifying which servers to include in
+ the returned service and where they should keep mail data.
+
+ @rtype: L{IService <twisted.application.service.IService>} provider
+ @return: A service which contains the requested mail servers.
+ """
+ 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)
+
+ if config["pop3"]:
+ f = config.service.getPOP3Factory()
+ for endpoint in config["pop3"]:
+ svc = internet.StreamServerEndpointService(endpoint, f)
+ svc.setServiceParent(config.service)
+
+ if config["smtp"]:
+ f = smtpFactory()
+ if config["hostname"]:
+ f.domain = config["hostname"]
+ f.fArgs = (f.domain,)
+ if config["esmtp"]:
+ f.fArgs = (None, None) + f.fArgs
+ for endpoint in config["smtp"]:
+ svc = internet.StreamServerEndpointService(endpoint, f)
+ svc.setServiceParent(config.service)
+
+ return config.service