summaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py3/twisted/mail/scripts
diff options
context:
space:
mode:
authorshmel1k <[email protected]>2023-11-26 18:16:14 +0300
committershmel1k <[email protected]>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py3/twisted/mail/scripts
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py3/twisted/mail/scripts')
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/scripts/__init__.py1
-rw-r--r--contrib/python/Twisted/py3/twisted/mail/scripts/mailmail.py386
2 files changed, 387 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py3/twisted/mail/scripts/__init__.py b/contrib/python/Twisted/py3/twisted/mail/scripts/__init__.py
new file mode 100644
index 00000000000..f653cc71edb
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/mail/scripts/__init__.py
@@ -0,0 +1 @@
+"mail scripts"
diff --git a/contrib/python/Twisted/py3/twisted/mail/scripts/mailmail.py b/contrib/python/Twisted/py3/twisted/mail/scripts/mailmail.py
new file mode 100644
index 00000000000..cf1e58f5b6b
--- /dev/null
+++ b/contrib/python/Twisted/py3/twisted/mail/scripts/mailmail.py
@@ -0,0 +1,386 @@
+# -*- test-case-name: twisted.mail.test.test_mailmail -*-
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Implementation module for the I{mailmail} command.
+"""
+
+
+import email.utils
+import getpass
+import os
+import sys
+from configparser import ConfigParser
+from io import StringIO
+
+from twisted.copyright import version
+from twisted.internet import reactor
+from twisted.logger import Logger, textFileLogObserver
+from twisted.mail import smtp
+
+GLOBAL_CFG = "/etc/mailmail"
+LOCAL_CFG = os.path.expanduser("~/.twisted/mailmail")
+SMARTHOST = "127.0.0.1"
+
+ERROR_FMT = """\
+Subject: Failed Message Delivery
+
+ Message delivery failed. The following occurred:
+
+ %s
+--
+The Twisted sendmail application.
+"""
+
+_logObserver = textFileLogObserver(sys.stderr)
+_log = Logger(observer=_logObserver)
+
+
+class Options:
+ """
+ Store the values of the parsed command-line options to the I{mailmail}
+ script.
+
+ @type to: L{list} of L{str}
+ @ivar to: The addresses to which to deliver this message.
+
+ @type sender: L{str}
+ @ivar sender: The address from which this message is being sent.
+
+ @type body: C{file}
+ @ivar body: The object from which the message is to be read.
+ """
+
+
+def getlogin():
+ try:
+ return os.getlogin()
+ except BaseException:
+ return getpass.getuser()
+
+
+_unsupportedOption = SystemExit("Unsupported option.")
+
+
+def parseOptions(argv):
+ o = Options()
+ o.to = [e for e in argv if not e.startswith("-")]
+ o.sender = getlogin()
+
+ # Just be very stupid
+
+ # Skip -bm -- it is the default
+
+ # Add a non-standard option for querying the version of this tool.
+ if "--version" in argv:
+ print("mailmail version:", version)
+ raise SystemExit()
+
+ # -bp lists queue information. Screw that.
+ if "-bp" in argv:
+ raise _unsupportedOption
+
+ # -bs makes sendmail use stdin/stdout as its transport. Screw that.
+ if "-bs" in argv:
+ raise _unsupportedOption
+
+ # -F sets who the mail is from, but is overridable by the From header
+ if "-F" in argv:
+ o.sender = argv[argv.index("-F") + 1]
+ o.to.remove(o.sender)
+
+ # -i and -oi makes us ignore lone "."
+ if ("-i" in argv) or ("-oi" in argv):
+ raise _unsupportedOption
+
+ # -odb is background delivery
+ if "-odb" in argv:
+ o.background = True
+ else:
+ o.background = False
+
+ # -odf is foreground delivery
+ if "-odf" in argv:
+ o.background = False
+ else:
+ o.background = True
+
+ # -oem and -em cause errors to be mailed back to the sender.
+ # It is also the default.
+
+ # -oep and -ep cause errors to be printed to stderr
+ if ("-oep" in argv) or ("-ep" in argv):
+ o.printErrors = True
+ else:
+ o.printErrors = False
+
+ # -om causes a copy of the message to be sent to the sender if the sender
+ # appears in an alias expansion. We do not support aliases.
+ if "-om" in argv:
+ raise _unsupportedOption
+
+ # -t causes us to pick the recipients of the message from
+ # the To, Cc, and Bcc headers, and to remove the Bcc header
+ # if present.
+ if "-t" in argv:
+ o.recipientsFromHeaders = True
+ o.excludeAddresses = o.to
+ o.to = []
+ else:
+ o.recipientsFromHeaders = False
+ o.exludeAddresses = []
+
+ requiredHeaders = {
+ "from": [],
+ "to": [],
+ "cc": [],
+ "bcc": [],
+ "date": [],
+ }
+
+ buffer = StringIO()
+ while 1:
+ write = 1
+ line = sys.stdin.readline()
+ if not line.strip():
+ break
+
+ hdrs = line.split(": ", 1)
+
+ hdr = hdrs[0].lower()
+ if o.recipientsFromHeaders and hdr in ("to", "cc", "bcc"):
+ o.to.extend([email.utils.parseaddr(hdrs[1])[1]])
+ if hdr == "bcc":
+ write = 0
+ elif hdr == "from":
+ o.sender = email.utils.parseaddr(hdrs[1])[1]
+
+ if hdr in requiredHeaders:
+ requiredHeaders[hdr].append(hdrs[1])
+
+ if write:
+ buffer.write(line)
+
+ if not requiredHeaders["from"]:
+ buffer.write(f"From: {o.sender}\r\n")
+ if not requiredHeaders["to"]:
+ if not o.to:
+ raise SystemExit("No recipients specified.")
+ buffer.write("To: {}\r\n".format(", ".join(o.to)))
+ if not requiredHeaders["date"]:
+ buffer.write(f"Date: {smtp.rfc822date()}\r\n")
+
+ buffer.write(line)
+
+ if o.recipientsFromHeaders:
+ for a in o.excludeAddresses:
+ try:
+ o.to.remove(a)
+ except BaseException:
+ pass
+
+ buffer.seek(0, 0)
+ o.body = StringIO(buffer.getvalue() + sys.stdin.read())
+ return o
+
+
+class Configuration:
+ """
+
+ @ivar allowUIDs: A list of UIDs which are allowed to send mail.
+ @ivar allowGIDs: A list of GIDs which are allowed to send mail.
+ @ivar denyUIDs: A list of UIDs which are not allowed to send mail.
+ @ivar denyGIDs: A list of GIDs which are not allowed to send mail.
+
+ @type defaultAccess: L{bool}
+ @ivar defaultAccess: L{True} if access will be allowed when no other access
+ control rule matches or L{False} if it will be denied in that case.
+
+ @ivar useraccess: Either C{'allow'} to check C{allowUID} first
+ or C{'deny'} to check C{denyUID} first.
+
+ @ivar groupaccess: Either C{'allow'} to check C{allowGID} first or
+ C{'deny'} to check C{denyGID} first.
+
+ @ivar identities: A L{dict} mapping hostnames to credentials to use when
+ sending mail to that host.
+
+ @ivar smarthost: L{None} or a hostname through which all outgoing mail will
+ be sent.
+
+ @ivar domain: L{None} or the hostname with which to identify ourselves when
+ connecting to an MTA.
+ """
+
+ def __init__(self):
+ self.allowUIDs = []
+ self.denyUIDs = []
+ self.allowGIDs = []
+ self.denyGIDs = []
+ self.useraccess = "deny"
+ self.groupaccess = "deny"
+
+ self.identities = {}
+ self.smarthost = None
+ self.domain = None
+
+ self.defaultAccess = True
+
+
+def loadConfig(path):
+ # [useraccess]
+ # allow=uid1,uid2,...
+ # deny=uid1,uid2,...
+ # order=allow,deny
+ # [groupaccess]
+ # allow=gid1,gid2,...
+ # deny=gid1,gid2,...
+ # order=deny,allow
+ # [identity]
+ # host1=username:password
+ # host2=username:password
+ # [addresses]
+ # smarthost=a.b.c.d
+ # default_domain=x.y.z
+
+ c = Configuration()
+
+ if not os.access(path, os.R_OK):
+ return c
+
+ p = ConfigParser()
+ p.read(path)
+
+ au = c.allowUIDs
+ du = c.denyUIDs
+ ag = c.allowGIDs
+ dg = c.denyGIDs
+ for section, a, d in (("useraccess", au, du), ("groupaccess", ag, dg)):
+ if p.has_section(section):
+ for mode, L in (("allow", a), ("deny", d)):
+ if p.has_option(section, mode) and p.get(section, mode):
+ for sectionID in p.get(section, mode).split(","):
+ try:
+ sectionID = int(sectionID)
+ except ValueError:
+ _log.error(
+ "Illegal {prefix}ID in "
+ "[{section}] section: {sectionID}",
+ prefix=section[0].upper(),
+ section=section,
+ sectionID=sectionID,
+ )
+ else:
+ L.append(sectionID)
+ order = p.get(section, "order")
+ order = [s.split() for s in [s.lower() for s in order.split(",")]]
+ if order[0] == "allow":
+ setattr(c, section, "allow")
+ else:
+ setattr(c, section, "deny")
+
+ if p.has_section("identity"):
+ for host, up in p.items("identity"):
+ parts = up.split(":", 1)
+ if len(parts) != 2:
+ _log.error("Illegal entry in [identity] section: {section}", section=up)
+ continue
+ c.identities[host] = parts
+
+ if p.has_section("addresses"):
+ if p.has_option("addresses", "smarthost"):
+ c.smarthost = p.get("addresses", "smarthost")
+ if p.has_option("addresses", "default_domain"):
+ c.domain = p.get("addresses", "default_domain")
+
+ return c
+
+
+def success(result):
+ reactor.stop()
+
+
+failed = None
+
+
+def failure(f):
+ global failed
+ reactor.stop()
+ failed = f
+
+
+def sendmail(host, options, ident):
+ d = smtp.sendmail(host, options.sender, options.to, options.body)
+ d.addCallbacks(success, failure)
+ reactor.run()
+
+
+def senderror(failure, options):
+ recipient = [options.sender]
+ sender = '"Internally Generated Message ({})"<postmaster@{}>'.format(
+ sys.argv[0], smtp.DNSNAME.decode("ascii")
+ )
+ error = StringIO()
+ failure.printTraceback(file=error)
+ body = StringIO(ERROR_FMT % error.getvalue())
+ d = smtp.sendmail("localhost", sender, recipient, body)
+ d.addBoth(lambda _: reactor.stop())
+
+
+def deny(conf):
+ uid = os.getuid()
+ gid = os.getgid()
+
+ if conf.useraccess == "deny":
+ if uid in conf.denyUIDs:
+ return True
+ if uid in conf.allowUIDs:
+ return False
+ else:
+ if uid in conf.allowUIDs:
+ return False
+ if uid in conf.denyUIDs:
+ return True
+
+ if conf.groupaccess == "deny":
+ if gid in conf.denyGIDs:
+ return True
+ if gid in conf.allowGIDs:
+ return False
+ else:
+ if gid in conf.allowGIDs:
+ return False
+ if gid in conf.denyGIDs:
+ return True
+
+ return not conf.defaultAccess
+
+
+def run():
+ o = parseOptions(sys.argv[1:])
+ gConf = loadConfig(GLOBAL_CFG)
+ lConf = loadConfig(LOCAL_CFG)
+
+ if deny(gConf) or deny(lConf):
+ _log.error("Permission denied")
+ return
+
+ host = lConf.smarthost or gConf.smarthost or SMARTHOST
+
+ ident = gConf.identities.copy()
+ ident.update(lConf.identities)
+
+ if lConf.domain:
+ smtp.DNSNAME = lConf.domain
+ elif gConf.domain:
+ smtp.DNSNAME = gConf.domain
+
+ sendmail(host, o, ident)
+
+ if failed:
+ if o.printErrors:
+ failed.printTraceback(file=sys.stderr)
+ raise SystemExit(1)
+ else:
+ senderror(failed, o)