diff options
author | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:16:14 +0300 |
---|---|---|
committer | shmel1k <shmel1k@ydb.tech> | 2023-11-26 18:43:30 +0300 |
commit | b8cf9e88f4c5c64d9406af533d8948deb050d695 (patch) | |
tree | 218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py3/twisted/mail/tap.py | |
parent | 523f645a83a0ec97a0332dbc3863bb354c92a328 (diff) | |
download | ydb-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.py | 384 |
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 |