1*cda5da8dSAndroid Build Coastguard Worker#! /usr/bin/env python3 2*cda5da8dSAndroid Build Coastguard Worker"""An RFC 5321 smtp proxy with optional RFC 1870 and RFC 6531 extensions. 3*cda5da8dSAndroid Build Coastguard Worker 4*cda5da8dSAndroid Build Coastguard WorkerUsage: %(program)s [options] [localhost:localport [remotehost:remoteport]] 5*cda5da8dSAndroid Build Coastguard Worker 6*cda5da8dSAndroid Build Coastguard WorkerOptions: 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Worker --nosetuid 9*cda5da8dSAndroid Build Coastguard Worker -n 10*cda5da8dSAndroid Build Coastguard Worker This program generally tries to setuid `nobody', unless this flag is 11*cda5da8dSAndroid Build Coastguard Worker set. The setuid call will fail if this program is not run as root (in 12*cda5da8dSAndroid Build Coastguard Worker which case, use this flag). 13*cda5da8dSAndroid Build Coastguard Worker 14*cda5da8dSAndroid Build Coastguard Worker --version 15*cda5da8dSAndroid Build Coastguard Worker -V 16*cda5da8dSAndroid Build Coastguard Worker Print the version number and exit. 17*cda5da8dSAndroid Build Coastguard Worker 18*cda5da8dSAndroid Build Coastguard Worker --class classname 19*cda5da8dSAndroid Build Coastguard Worker -c classname 20*cda5da8dSAndroid Build Coastguard Worker Use `classname' as the concrete SMTP proxy class. Uses `PureProxy' by 21*cda5da8dSAndroid Build Coastguard Worker default. 22*cda5da8dSAndroid Build Coastguard Worker 23*cda5da8dSAndroid Build Coastguard Worker --size limit 24*cda5da8dSAndroid Build Coastguard Worker -s limit 25*cda5da8dSAndroid Build Coastguard Worker Restrict the total size of the incoming message to "limit" number of 26*cda5da8dSAndroid Build Coastguard Worker bytes via the RFC 1870 SIZE extension. Defaults to 33554432 bytes. 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Worker --smtputf8 29*cda5da8dSAndroid Build Coastguard Worker -u 30*cda5da8dSAndroid Build Coastguard Worker Enable the SMTPUTF8 extension and behave as an RFC 6531 smtp proxy. 31*cda5da8dSAndroid Build Coastguard Worker 32*cda5da8dSAndroid Build Coastguard Worker --debug 33*cda5da8dSAndroid Build Coastguard Worker -d 34*cda5da8dSAndroid Build Coastguard Worker Turn on debugging prints. 35*cda5da8dSAndroid Build Coastguard Worker 36*cda5da8dSAndroid Build Coastguard Worker --help 37*cda5da8dSAndroid Build Coastguard Worker -h 38*cda5da8dSAndroid Build Coastguard Worker Print this message and exit. 39*cda5da8dSAndroid Build Coastguard Worker 40*cda5da8dSAndroid Build Coastguard WorkerVersion: %(__version__)s 41*cda5da8dSAndroid Build Coastguard Worker 42*cda5da8dSAndroid Build Coastguard WorkerIf localhost is not given then `localhost' is used, and if localport is not 43*cda5da8dSAndroid Build Coastguard Workergiven then 8025 is used. If remotehost is not given then `localhost' is used, 44*cda5da8dSAndroid Build Coastguard Workerand if remoteport is not given, then 25 is used. 45*cda5da8dSAndroid Build Coastguard Worker""" 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker# Overview: 48*cda5da8dSAndroid Build Coastguard Worker# 49*cda5da8dSAndroid Build Coastguard Worker# This file implements the minimal SMTP protocol as defined in RFC 5321. It 50*cda5da8dSAndroid Build Coastguard Worker# has a hierarchy of classes which implement the backend functionality for the 51*cda5da8dSAndroid Build Coastguard Worker# smtpd. A number of classes are provided: 52*cda5da8dSAndroid Build Coastguard Worker# 53*cda5da8dSAndroid Build Coastguard Worker# SMTPServer - the base class for the backend. Raises NotImplementedError 54*cda5da8dSAndroid Build Coastguard Worker# if you try to use it. 55*cda5da8dSAndroid Build Coastguard Worker# 56*cda5da8dSAndroid Build Coastguard Worker# DebuggingServer - simply prints each message it receives on stdout. 57*cda5da8dSAndroid Build Coastguard Worker# 58*cda5da8dSAndroid Build Coastguard Worker# PureProxy - Proxies all messages to a real smtpd which does final 59*cda5da8dSAndroid Build Coastguard Worker# delivery. One known problem with this class is that it doesn't handle 60*cda5da8dSAndroid Build Coastguard Worker# SMTP errors from the backend server at all. This should be fixed 61*cda5da8dSAndroid Build Coastguard Worker# (contributions are welcome!). 62*cda5da8dSAndroid Build Coastguard Worker# 63*cda5da8dSAndroid Build Coastguard Worker# 64*cda5da8dSAndroid Build Coastguard Worker# Author: Barry Warsaw <[email protected]> 65*cda5da8dSAndroid Build Coastguard Worker# 66*cda5da8dSAndroid Build Coastguard Worker# TODO: 67*cda5da8dSAndroid Build Coastguard Worker# 68*cda5da8dSAndroid Build Coastguard Worker# - support mailbox delivery 69*cda5da8dSAndroid Build Coastguard Worker# - alias files 70*cda5da8dSAndroid Build Coastguard Worker# - Handle more ESMTP extensions 71*cda5da8dSAndroid Build Coastguard Worker# - handle error codes from the backend smtpd 72*cda5da8dSAndroid Build Coastguard Worker 73*cda5da8dSAndroid Build Coastguard Workerimport sys 74*cda5da8dSAndroid Build Coastguard Workerimport os 75*cda5da8dSAndroid Build Coastguard Workerimport errno 76*cda5da8dSAndroid Build Coastguard Workerimport getopt 77*cda5da8dSAndroid Build Coastguard Workerimport time 78*cda5da8dSAndroid Build Coastguard Workerimport socket 79*cda5da8dSAndroid Build Coastguard Workerimport collections 80*cda5da8dSAndroid Build Coastguard Workerfrom warnings import _deprecated, warn 81*cda5da8dSAndroid Build Coastguard Workerfrom email._header_value_parser import get_addr_spec, get_angle_addr 82*cda5da8dSAndroid Build Coastguard Worker 83*cda5da8dSAndroid Build Coastguard Worker__all__ = [ 84*cda5da8dSAndroid Build Coastguard Worker "SMTPChannel", "SMTPServer", "DebuggingServer", "PureProxy", 85*cda5da8dSAndroid Build Coastguard Worker] 86*cda5da8dSAndroid Build Coastguard Worker 87*cda5da8dSAndroid Build Coastguard Worker_DEPRECATION_MSG = ('The {name} module is deprecated and unmaintained and will ' 88*cda5da8dSAndroid Build Coastguard Worker 'be removed in Python {remove}. Please see aiosmtpd ' 89*cda5da8dSAndroid Build Coastguard Worker '(https://aiosmtpd.readthedocs.io/) for the recommended ' 90*cda5da8dSAndroid Build Coastguard Worker 'replacement.') 91*cda5da8dSAndroid Build Coastguard Worker_deprecated(__name__, _DEPRECATION_MSG, remove=(3, 12)) 92*cda5da8dSAndroid Build Coastguard Worker 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Worker# These are imported after the above warning so that users get the correct 95*cda5da8dSAndroid Build Coastguard Worker# deprecation warning. 96*cda5da8dSAndroid Build Coastguard Workerimport asyncore 97*cda5da8dSAndroid Build Coastguard Workerimport asynchat 98*cda5da8dSAndroid Build Coastguard Worker 99*cda5da8dSAndroid Build Coastguard Worker 100*cda5da8dSAndroid Build Coastguard Workerprogram = sys.argv[0] 101*cda5da8dSAndroid Build Coastguard Worker__version__ = 'Python SMTP proxy version 0.3' 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker 104*cda5da8dSAndroid Build Coastguard Workerclass Devnull: 105*cda5da8dSAndroid Build Coastguard Worker def write(self, msg): pass 106*cda5da8dSAndroid Build Coastguard Worker def flush(self): pass 107*cda5da8dSAndroid Build Coastguard Worker 108*cda5da8dSAndroid Build Coastguard Worker 109*cda5da8dSAndroid Build Coastguard WorkerDEBUGSTREAM = Devnull() 110*cda5da8dSAndroid Build Coastguard WorkerNEWLINE = '\n' 111*cda5da8dSAndroid Build Coastguard WorkerCOMMASPACE = ', ' 112*cda5da8dSAndroid Build Coastguard WorkerDATA_SIZE_DEFAULT = 33554432 113*cda5da8dSAndroid Build Coastguard Worker 114*cda5da8dSAndroid Build Coastguard Worker 115*cda5da8dSAndroid Build Coastguard Workerdef usage(code, msg=''): 116*cda5da8dSAndroid Build Coastguard Worker print(__doc__ % globals(), file=sys.stderr) 117*cda5da8dSAndroid Build Coastguard Worker if msg: 118*cda5da8dSAndroid Build Coastguard Worker print(msg, file=sys.stderr) 119*cda5da8dSAndroid Build Coastguard Worker sys.exit(code) 120*cda5da8dSAndroid Build Coastguard Worker 121*cda5da8dSAndroid Build Coastguard Worker 122*cda5da8dSAndroid Build Coastguard Workerclass SMTPChannel(asynchat.async_chat): 123*cda5da8dSAndroid Build Coastguard Worker COMMAND = 0 124*cda5da8dSAndroid Build Coastguard Worker DATA = 1 125*cda5da8dSAndroid Build Coastguard Worker 126*cda5da8dSAndroid Build Coastguard Worker command_size_limit = 512 127*cda5da8dSAndroid Build Coastguard Worker command_size_limits = collections.defaultdict(lambda x=command_size_limit: x) 128*cda5da8dSAndroid Build Coastguard Worker 129*cda5da8dSAndroid Build Coastguard Worker @property 130*cda5da8dSAndroid Build Coastguard Worker def max_command_size_limit(self): 131*cda5da8dSAndroid Build Coastguard Worker try: 132*cda5da8dSAndroid Build Coastguard Worker return max(self.command_size_limits.values()) 133*cda5da8dSAndroid Build Coastguard Worker except ValueError: 134*cda5da8dSAndroid Build Coastguard Worker return self.command_size_limit 135*cda5da8dSAndroid Build Coastguard Worker 136*cda5da8dSAndroid Build Coastguard Worker def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT, 137*cda5da8dSAndroid Build Coastguard Worker map=None, enable_SMTPUTF8=False, decode_data=False): 138*cda5da8dSAndroid Build Coastguard Worker asynchat.async_chat.__init__(self, conn, map=map) 139*cda5da8dSAndroid Build Coastguard Worker self.smtp_server = server 140*cda5da8dSAndroid Build Coastguard Worker self.conn = conn 141*cda5da8dSAndroid Build Coastguard Worker self.addr = addr 142*cda5da8dSAndroid Build Coastguard Worker self.data_size_limit = data_size_limit 143*cda5da8dSAndroid Build Coastguard Worker self.enable_SMTPUTF8 = enable_SMTPUTF8 144*cda5da8dSAndroid Build Coastguard Worker self._decode_data = decode_data 145*cda5da8dSAndroid Build Coastguard Worker if enable_SMTPUTF8 and decode_data: 146*cda5da8dSAndroid Build Coastguard Worker raise ValueError("decode_data and enable_SMTPUTF8 cannot" 147*cda5da8dSAndroid Build Coastguard Worker " be set to True at the same time") 148*cda5da8dSAndroid Build Coastguard Worker if decode_data: 149*cda5da8dSAndroid Build Coastguard Worker self._emptystring = '' 150*cda5da8dSAndroid Build Coastguard Worker self._linesep = '\r\n' 151*cda5da8dSAndroid Build Coastguard Worker self._dotsep = '.' 152*cda5da8dSAndroid Build Coastguard Worker self._newline = NEWLINE 153*cda5da8dSAndroid Build Coastguard Worker else: 154*cda5da8dSAndroid Build Coastguard Worker self._emptystring = b'' 155*cda5da8dSAndroid Build Coastguard Worker self._linesep = b'\r\n' 156*cda5da8dSAndroid Build Coastguard Worker self._dotsep = ord(b'.') 157*cda5da8dSAndroid Build Coastguard Worker self._newline = b'\n' 158*cda5da8dSAndroid Build Coastguard Worker self._set_rset_state() 159*cda5da8dSAndroid Build Coastguard Worker self.seen_greeting = '' 160*cda5da8dSAndroid Build Coastguard Worker self.extended_smtp = False 161*cda5da8dSAndroid Build Coastguard Worker self.command_size_limits.clear() 162*cda5da8dSAndroid Build Coastguard Worker self.fqdn = socket.getfqdn() 163*cda5da8dSAndroid Build Coastguard Worker try: 164*cda5da8dSAndroid Build Coastguard Worker self.peer = conn.getpeername() 165*cda5da8dSAndroid Build Coastguard Worker except OSError as err: 166*cda5da8dSAndroid Build Coastguard Worker # a race condition may occur if the other end is closing 167*cda5da8dSAndroid Build Coastguard Worker # before we can get the peername 168*cda5da8dSAndroid Build Coastguard Worker self.close() 169*cda5da8dSAndroid Build Coastguard Worker if err.errno != errno.ENOTCONN: 170*cda5da8dSAndroid Build Coastguard Worker raise 171*cda5da8dSAndroid Build Coastguard Worker return 172*cda5da8dSAndroid Build Coastguard Worker print('Peer:', repr(self.peer), file=DEBUGSTREAM) 173*cda5da8dSAndroid Build Coastguard Worker self.push('220 %s %s' % (self.fqdn, __version__)) 174*cda5da8dSAndroid Build Coastguard Worker 175*cda5da8dSAndroid Build Coastguard Worker def _set_post_data_state(self): 176*cda5da8dSAndroid Build Coastguard Worker """Reset state variables to their post-DATA state.""" 177*cda5da8dSAndroid Build Coastguard Worker self.smtp_state = self.COMMAND 178*cda5da8dSAndroid Build Coastguard Worker self.mailfrom = None 179*cda5da8dSAndroid Build Coastguard Worker self.rcpttos = [] 180*cda5da8dSAndroid Build Coastguard Worker self.require_SMTPUTF8 = False 181*cda5da8dSAndroid Build Coastguard Worker self.num_bytes = 0 182*cda5da8dSAndroid Build Coastguard Worker self.set_terminator(b'\r\n') 183*cda5da8dSAndroid Build Coastguard Worker 184*cda5da8dSAndroid Build Coastguard Worker def _set_rset_state(self): 185*cda5da8dSAndroid Build Coastguard Worker """Reset all state variables except the greeting.""" 186*cda5da8dSAndroid Build Coastguard Worker self._set_post_data_state() 187*cda5da8dSAndroid Build Coastguard Worker self.received_data = '' 188*cda5da8dSAndroid Build Coastguard Worker self.received_lines = [] 189*cda5da8dSAndroid Build Coastguard Worker 190*cda5da8dSAndroid Build Coastguard Worker 191*cda5da8dSAndroid Build Coastguard Worker # properties for backwards-compatibility 192*cda5da8dSAndroid Build Coastguard Worker @property 193*cda5da8dSAndroid Build Coastguard Worker def __server(self): 194*cda5da8dSAndroid Build Coastguard Worker warn("Access to __server attribute on SMTPChannel is deprecated, " 195*cda5da8dSAndroid Build Coastguard Worker "use 'smtp_server' instead", DeprecationWarning, 2) 196*cda5da8dSAndroid Build Coastguard Worker return self.smtp_server 197*cda5da8dSAndroid Build Coastguard Worker @__server.setter 198*cda5da8dSAndroid Build Coastguard Worker def __server(self, value): 199*cda5da8dSAndroid Build Coastguard Worker warn("Setting __server attribute on SMTPChannel is deprecated, " 200*cda5da8dSAndroid Build Coastguard Worker "set 'smtp_server' instead", DeprecationWarning, 2) 201*cda5da8dSAndroid Build Coastguard Worker self.smtp_server = value 202*cda5da8dSAndroid Build Coastguard Worker 203*cda5da8dSAndroid Build Coastguard Worker @property 204*cda5da8dSAndroid Build Coastguard Worker def __line(self): 205*cda5da8dSAndroid Build Coastguard Worker warn("Access to __line attribute on SMTPChannel is deprecated, " 206*cda5da8dSAndroid Build Coastguard Worker "use 'received_lines' instead", DeprecationWarning, 2) 207*cda5da8dSAndroid Build Coastguard Worker return self.received_lines 208*cda5da8dSAndroid Build Coastguard Worker @__line.setter 209*cda5da8dSAndroid Build Coastguard Worker def __line(self, value): 210*cda5da8dSAndroid Build Coastguard Worker warn("Setting __line attribute on SMTPChannel is deprecated, " 211*cda5da8dSAndroid Build Coastguard Worker "set 'received_lines' instead", DeprecationWarning, 2) 212*cda5da8dSAndroid Build Coastguard Worker self.received_lines = value 213*cda5da8dSAndroid Build Coastguard Worker 214*cda5da8dSAndroid Build Coastguard Worker @property 215*cda5da8dSAndroid Build Coastguard Worker def __state(self): 216*cda5da8dSAndroid Build Coastguard Worker warn("Access to __state attribute on SMTPChannel is deprecated, " 217*cda5da8dSAndroid Build Coastguard Worker "use 'smtp_state' instead", DeprecationWarning, 2) 218*cda5da8dSAndroid Build Coastguard Worker return self.smtp_state 219*cda5da8dSAndroid Build Coastguard Worker @__state.setter 220*cda5da8dSAndroid Build Coastguard Worker def __state(self, value): 221*cda5da8dSAndroid Build Coastguard Worker warn("Setting __state attribute on SMTPChannel is deprecated, " 222*cda5da8dSAndroid Build Coastguard Worker "set 'smtp_state' instead", DeprecationWarning, 2) 223*cda5da8dSAndroid Build Coastguard Worker self.smtp_state = value 224*cda5da8dSAndroid Build Coastguard Worker 225*cda5da8dSAndroid Build Coastguard Worker @property 226*cda5da8dSAndroid Build Coastguard Worker def __greeting(self): 227*cda5da8dSAndroid Build Coastguard Worker warn("Access to __greeting attribute on SMTPChannel is deprecated, " 228*cda5da8dSAndroid Build Coastguard Worker "use 'seen_greeting' instead", DeprecationWarning, 2) 229*cda5da8dSAndroid Build Coastguard Worker return self.seen_greeting 230*cda5da8dSAndroid Build Coastguard Worker @__greeting.setter 231*cda5da8dSAndroid Build Coastguard Worker def __greeting(self, value): 232*cda5da8dSAndroid Build Coastguard Worker warn("Setting __greeting attribute on SMTPChannel is deprecated, " 233*cda5da8dSAndroid Build Coastguard Worker "set 'seen_greeting' instead", DeprecationWarning, 2) 234*cda5da8dSAndroid Build Coastguard Worker self.seen_greeting = value 235*cda5da8dSAndroid Build Coastguard Worker 236*cda5da8dSAndroid Build Coastguard Worker @property 237*cda5da8dSAndroid Build Coastguard Worker def __mailfrom(self): 238*cda5da8dSAndroid Build Coastguard Worker warn("Access to __mailfrom attribute on SMTPChannel is deprecated, " 239*cda5da8dSAndroid Build Coastguard Worker "use 'mailfrom' instead", DeprecationWarning, 2) 240*cda5da8dSAndroid Build Coastguard Worker return self.mailfrom 241*cda5da8dSAndroid Build Coastguard Worker @__mailfrom.setter 242*cda5da8dSAndroid Build Coastguard Worker def __mailfrom(self, value): 243*cda5da8dSAndroid Build Coastguard Worker warn("Setting __mailfrom attribute on SMTPChannel is deprecated, " 244*cda5da8dSAndroid Build Coastguard Worker "set 'mailfrom' instead", DeprecationWarning, 2) 245*cda5da8dSAndroid Build Coastguard Worker self.mailfrom = value 246*cda5da8dSAndroid Build Coastguard Worker 247*cda5da8dSAndroid Build Coastguard Worker @property 248*cda5da8dSAndroid Build Coastguard Worker def __rcpttos(self): 249*cda5da8dSAndroid Build Coastguard Worker warn("Access to __rcpttos attribute on SMTPChannel is deprecated, " 250*cda5da8dSAndroid Build Coastguard Worker "use 'rcpttos' instead", DeprecationWarning, 2) 251*cda5da8dSAndroid Build Coastguard Worker return self.rcpttos 252*cda5da8dSAndroid Build Coastguard Worker @__rcpttos.setter 253*cda5da8dSAndroid Build Coastguard Worker def __rcpttos(self, value): 254*cda5da8dSAndroid Build Coastguard Worker warn("Setting __rcpttos attribute on SMTPChannel is deprecated, " 255*cda5da8dSAndroid Build Coastguard Worker "set 'rcpttos' instead", DeprecationWarning, 2) 256*cda5da8dSAndroid Build Coastguard Worker self.rcpttos = value 257*cda5da8dSAndroid Build Coastguard Worker 258*cda5da8dSAndroid Build Coastguard Worker @property 259*cda5da8dSAndroid Build Coastguard Worker def __data(self): 260*cda5da8dSAndroid Build Coastguard Worker warn("Access to __data attribute on SMTPChannel is deprecated, " 261*cda5da8dSAndroid Build Coastguard Worker "use 'received_data' instead", DeprecationWarning, 2) 262*cda5da8dSAndroid Build Coastguard Worker return self.received_data 263*cda5da8dSAndroid Build Coastguard Worker @__data.setter 264*cda5da8dSAndroid Build Coastguard Worker def __data(self, value): 265*cda5da8dSAndroid Build Coastguard Worker warn("Setting __data attribute on SMTPChannel is deprecated, " 266*cda5da8dSAndroid Build Coastguard Worker "set 'received_data' instead", DeprecationWarning, 2) 267*cda5da8dSAndroid Build Coastguard Worker self.received_data = value 268*cda5da8dSAndroid Build Coastguard Worker 269*cda5da8dSAndroid Build Coastguard Worker @property 270*cda5da8dSAndroid Build Coastguard Worker def __fqdn(self): 271*cda5da8dSAndroid Build Coastguard Worker warn("Access to __fqdn attribute on SMTPChannel is deprecated, " 272*cda5da8dSAndroid Build Coastguard Worker "use 'fqdn' instead", DeprecationWarning, 2) 273*cda5da8dSAndroid Build Coastguard Worker return self.fqdn 274*cda5da8dSAndroid Build Coastguard Worker @__fqdn.setter 275*cda5da8dSAndroid Build Coastguard Worker def __fqdn(self, value): 276*cda5da8dSAndroid Build Coastguard Worker warn("Setting __fqdn attribute on SMTPChannel is deprecated, " 277*cda5da8dSAndroid Build Coastguard Worker "set 'fqdn' instead", DeprecationWarning, 2) 278*cda5da8dSAndroid Build Coastguard Worker self.fqdn = value 279*cda5da8dSAndroid Build Coastguard Worker 280*cda5da8dSAndroid Build Coastguard Worker @property 281*cda5da8dSAndroid Build Coastguard Worker def __peer(self): 282*cda5da8dSAndroid Build Coastguard Worker warn("Access to __peer attribute on SMTPChannel is deprecated, " 283*cda5da8dSAndroid Build Coastguard Worker "use 'peer' instead", DeprecationWarning, 2) 284*cda5da8dSAndroid Build Coastguard Worker return self.peer 285*cda5da8dSAndroid Build Coastguard Worker @__peer.setter 286*cda5da8dSAndroid Build Coastguard Worker def __peer(self, value): 287*cda5da8dSAndroid Build Coastguard Worker warn("Setting __peer attribute on SMTPChannel is deprecated, " 288*cda5da8dSAndroid Build Coastguard Worker "set 'peer' instead", DeprecationWarning, 2) 289*cda5da8dSAndroid Build Coastguard Worker self.peer = value 290*cda5da8dSAndroid Build Coastguard Worker 291*cda5da8dSAndroid Build Coastguard Worker @property 292*cda5da8dSAndroid Build Coastguard Worker def __conn(self): 293*cda5da8dSAndroid Build Coastguard Worker warn("Access to __conn attribute on SMTPChannel is deprecated, " 294*cda5da8dSAndroid Build Coastguard Worker "use 'conn' instead", DeprecationWarning, 2) 295*cda5da8dSAndroid Build Coastguard Worker return self.conn 296*cda5da8dSAndroid Build Coastguard Worker @__conn.setter 297*cda5da8dSAndroid Build Coastguard Worker def __conn(self, value): 298*cda5da8dSAndroid Build Coastguard Worker warn("Setting __conn attribute on SMTPChannel is deprecated, " 299*cda5da8dSAndroid Build Coastguard Worker "set 'conn' instead", DeprecationWarning, 2) 300*cda5da8dSAndroid Build Coastguard Worker self.conn = value 301*cda5da8dSAndroid Build Coastguard Worker 302*cda5da8dSAndroid Build Coastguard Worker @property 303*cda5da8dSAndroid Build Coastguard Worker def __addr(self): 304*cda5da8dSAndroid Build Coastguard Worker warn("Access to __addr attribute on SMTPChannel is deprecated, " 305*cda5da8dSAndroid Build Coastguard Worker "use 'addr' instead", DeprecationWarning, 2) 306*cda5da8dSAndroid Build Coastguard Worker return self.addr 307*cda5da8dSAndroid Build Coastguard Worker @__addr.setter 308*cda5da8dSAndroid Build Coastguard Worker def __addr(self, value): 309*cda5da8dSAndroid Build Coastguard Worker warn("Setting __addr attribute on SMTPChannel is deprecated, " 310*cda5da8dSAndroid Build Coastguard Worker "set 'addr' instead", DeprecationWarning, 2) 311*cda5da8dSAndroid Build Coastguard Worker self.addr = value 312*cda5da8dSAndroid Build Coastguard Worker 313*cda5da8dSAndroid Build Coastguard Worker # Overrides base class for convenience. 314*cda5da8dSAndroid Build Coastguard Worker def push(self, msg): 315*cda5da8dSAndroid Build Coastguard Worker asynchat.async_chat.push(self, bytes( 316*cda5da8dSAndroid Build Coastguard Worker msg + '\r\n', 'utf-8' if self.require_SMTPUTF8 else 'ascii')) 317*cda5da8dSAndroid Build Coastguard Worker 318*cda5da8dSAndroid Build Coastguard Worker # Implementation of base class abstract method 319*cda5da8dSAndroid Build Coastguard Worker def collect_incoming_data(self, data): 320*cda5da8dSAndroid Build Coastguard Worker limit = None 321*cda5da8dSAndroid Build Coastguard Worker if self.smtp_state == self.COMMAND: 322*cda5da8dSAndroid Build Coastguard Worker limit = self.max_command_size_limit 323*cda5da8dSAndroid Build Coastguard Worker elif self.smtp_state == self.DATA: 324*cda5da8dSAndroid Build Coastguard Worker limit = self.data_size_limit 325*cda5da8dSAndroid Build Coastguard Worker if limit and self.num_bytes > limit: 326*cda5da8dSAndroid Build Coastguard Worker return 327*cda5da8dSAndroid Build Coastguard Worker elif limit: 328*cda5da8dSAndroid Build Coastguard Worker self.num_bytes += len(data) 329*cda5da8dSAndroid Build Coastguard Worker if self._decode_data: 330*cda5da8dSAndroid Build Coastguard Worker self.received_lines.append(str(data, 'utf-8')) 331*cda5da8dSAndroid Build Coastguard Worker else: 332*cda5da8dSAndroid Build Coastguard Worker self.received_lines.append(data) 333*cda5da8dSAndroid Build Coastguard Worker 334*cda5da8dSAndroid Build Coastguard Worker # Implementation of base class abstract method 335*cda5da8dSAndroid Build Coastguard Worker def found_terminator(self): 336*cda5da8dSAndroid Build Coastguard Worker line = self._emptystring.join(self.received_lines) 337*cda5da8dSAndroid Build Coastguard Worker print('Data:', repr(line), file=DEBUGSTREAM) 338*cda5da8dSAndroid Build Coastguard Worker self.received_lines = [] 339*cda5da8dSAndroid Build Coastguard Worker if self.smtp_state == self.COMMAND: 340*cda5da8dSAndroid Build Coastguard Worker sz, self.num_bytes = self.num_bytes, 0 341*cda5da8dSAndroid Build Coastguard Worker if not line: 342*cda5da8dSAndroid Build Coastguard Worker self.push('500 Error: bad syntax') 343*cda5da8dSAndroid Build Coastguard Worker return 344*cda5da8dSAndroid Build Coastguard Worker if not self._decode_data: 345*cda5da8dSAndroid Build Coastguard Worker line = str(line, 'utf-8') 346*cda5da8dSAndroid Build Coastguard Worker i = line.find(' ') 347*cda5da8dSAndroid Build Coastguard Worker if i < 0: 348*cda5da8dSAndroid Build Coastguard Worker command = line.upper() 349*cda5da8dSAndroid Build Coastguard Worker arg = None 350*cda5da8dSAndroid Build Coastguard Worker else: 351*cda5da8dSAndroid Build Coastguard Worker command = line[:i].upper() 352*cda5da8dSAndroid Build Coastguard Worker arg = line[i+1:].strip() 353*cda5da8dSAndroid Build Coastguard Worker max_sz = (self.command_size_limits[command] 354*cda5da8dSAndroid Build Coastguard Worker if self.extended_smtp else self.command_size_limit) 355*cda5da8dSAndroid Build Coastguard Worker if sz > max_sz: 356*cda5da8dSAndroid Build Coastguard Worker self.push('500 Error: line too long') 357*cda5da8dSAndroid Build Coastguard Worker return 358*cda5da8dSAndroid Build Coastguard Worker method = getattr(self, 'smtp_' + command, None) 359*cda5da8dSAndroid Build Coastguard Worker if not method: 360*cda5da8dSAndroid Build Coastguard Worker self.push('500 Error: command "%s" not recognized' % command) 361*cda5da8dSAndroid Build Coastguard Worker return 362*cda5da8dSAndroid Build Coastguard Worker method(arg) 363*cda5da8dSAndroid Build Coastguard Worker return 364*cda5da8dSAndroid Build Coastguard Worker else: 365*cda5da8dSAndroid Build Coastguard Worker if self.smtp_state != self.DATA: 366*cda5da8dSAndroid Build Coastguard Worker self.push('451 Internal confusion') 367*cda5da8dSAndroid Build Coastguard Worker self.num_bytes = 0 368*cda5da8dSAndroid Build Coastguard Worker return 369*cda5da8dSAndroid Build Coastguard Worker if self.data_size_limit and self.num_bytes > self.data_size_limit: 370*cda5da8dSAndroid Build Coastguard Worker self.push('552 Error: Too much mail data') 371*cda5da8dSAndroid Build Coastguard Worker self.num_bytes = 0 372*cda5da8dSAndroid Build Coastguard Worker return 373*cda5da8dSAndroid Build Coastguard Worker # Remove extraneous carriage returns and de-transparency according 374*cda5da8dSAndroid Build Coastguard Worker # to RFC 5321, Section 4.5.2. 375*cda5da8dSAndroid Build Coastguard Worker data = [] 376*cda5da8dSAndroid Build Coastguard Worker for text in line.split(self._linesep): 377*cda5da8dSAndroid Build Coastguard Worker if text and text[0] == self._dotsep: 378*cda5da8dSAndroid Build Coastguard Worker data.append(text[1:]) 379*cda5da8dSAndroid Build Coastguard Worker else: 380*cda5da8dSAndroid Build Coastguard Worker data.append(text) 381*cda5da8dSAndroid Build Coastguard Worker self.received_data = self._newline.join(data) 382*cda5da8dSAndroid Build Coastguard Worker args = (self.peer, self.mailfrom, self.rcpttos, self.received_data) 383*cda5da8dSAndroid Build Coastguard Worker kwargs = {} 384*cda5da8dSAndroid Build Coastguard Worker if not self._decode_data: 385*cda5da8dSAndroid Build Coastguard Worker kwargs = { 386*cda5da8dSAndroid Build Coastguard Worker 'mail_options': self.mail_options, 387*cda5da8dSAndroid Build Coastguard Worker 'rcpt_options': self.rcpt_options, 388*cda5da8dSAndroid Build Coastguard Worker } 389*cda5da8dSAndroid Build Coastguard Worker status = self.smtp_server.process_message(*args, **kwargs) 390*cda5da8dSAndroid Build Coastguard Worker self._set_post_data_state() 391*cda5da8dSAndroid Build Coastguard Worker if not status: 392*cda5da8dSAndroid Build Coastguard Worker self.push('250 OK') 393*cda5da8dSAndroid Build Coastguard Worker else: 394*cda5da8dSAndroid Build Coastguard Worker self.push(status) 395*cda5da8dSAndroid Build Coastguard Worker 396*cda5da8dSAndroid Build Coastguard Worker # SMTP and ESMTP commands 397*cda5da8dSAndroid Build Coastguard Worker def smtp_HELO(self, arg): 398*cda5da8dSAndroid Build Coastguard Worker if not arg: 399*cda5da8dSAndroid Build Coastguard Worker self.push('501 Syntax: HELO hostname') 400*cda5da8dSAndroid Build Coastguard Worker return 401*cda5da8dSAndroid Build Coastguard Worker # See issue #21783 for a discussion of this behavior. 402*cda5da8dSAndroid Build Coastguard Worker if self.seen_greeting: 403*cda5da8dSAndroid Build Coastguard Worker self.push('503 Duplicate HELO/EHLO') 404*cda5da8dSAndroid Build Coastguard Worker return 405*cda5da8dSAndroid Build Coastguard Worker self._set_rset_state() 406*cda5da8dSAndroid Build Coastguard Worker self.seen_greeting = arg 407*cda5da8dSAndroid Build Coastguard Worker self.push('250 %s' % self.fqdn) 408*cda5da8dSAndroid Build Coastguard Worker 409*cda5da8dSAndroid Build Coastguard Worker def smtp_EHLO(self, arg): 410*cda5da8dSAndroid Build Coastguard Worker if not arg: 411*cda5da8dSAndroid Build Coastguard Worker self.push('501 Syntax: EHLO hostname') 412*cda5da8dSAndroid Build Coastguard Worker return 413*cda5da8dSAndroid Build Coastguard Worker # See issue #21783 for a discussion of this behavior. 414*cda5da8dSAndroid Build Coastguard Worker if self.seen_greeting: 415*cda5da8dSAndroid Build Coastguard Worker self.push('503 Duplicate HELO/EHLO') 416*cda5da8dSAndroid Build Coastguard Worker return 417*cda5da8dSAndroid Build Coastguard Worker self._set_rset_state() 418*cda5da8dSAndroid Build Coastguard Worker self.seen_greeting = arg 419*cda5da8dSAndroid Build Coastguard Worker self.extended_smtp = True 420*cda5da8dSAndroid Build Coastguard Worker self.push('250-%s' % self.fqdn) 421*cda5da8dSAndroid Build Coastguard Worker if self.data_size_limit: 422*cda5da8dSAndroid Build Coastguard Worker self.push('250-SIZE %s' % self.data_size_limit) 423*cda5da8dSAndroid Build Coastguard Worker self.command_size_limits['MAIL'] += 26 424*cda5da8dSAndroid Build Coastguard Worker if not self._decode_data: 425*cda5da8dSAndroid Build Coastguard Worker self.push('250-8BITMIME') 426*cda5da8dSAndroid Build Coastguard Worker if self.enable_SMTPUTF8: 427*cda5da8dSAndroid Build Coastguard Worker self.push('250-SMTPUTF8') 428*cda5da8dSAndroid Build Coastguard Worker self.command_size_limits['MAIL'] += 10 429*cda5da8dSAndroid Build Coastguard Worker self.push('250 HELP') 430*cda5da8dSAndroid Build Coastguard Worker 431*cda5da8dSAndroid Build Coastguard Worker def smtp_NOOP(self, arg): 432*cda5da8dSAndroid Build Coastguard Worker if arg: 433*cda5da8dSAndroid Build Coastguard Worker self.push('501 Syntax: NOOP') 434*cda5da8dSAndroid Build Coastguard Worker else: 435*cda5da8dSAndroid Build Coastguard Worker self.push('250 OK') 436*cda5da8dSAndroid Build Coastguard Worker 437*cda5da8dSAndroid Build Coastguard Worker def smtp_QUIT(self, arg): 438*cda5da8dSAndroid Build Coastguard Worker # args is ignored 439*cda5da8dSAndroid Build Coastguard Worker self.push('221 Bye') 440*cda5da8dSAndroid Build Coastguard Worker self.close_when_done() 441*cda5da8dSAndroid Build Coastguard Worker 442*cda5da8dSAndroid Build Coastguard Worker def _strip_command_keyword(self, keyword, arg): 443*cda5da8dSAndroid Build Coastguard Worker keylen = len(keyword) 444*cda5da8dSAndroid Build Coastguard Worker if arg[:keylen].upper() == keyword: 445*cda5da8dSAndroid Build Coastguard Worker return arg[keylen:].strip() 446*cda5da8dSAndroid Build Coastguard Worker return '' 447*cda5da8dSAndroid Build Coastguard Worker 448*cda5da8dSAndroid Build Coastguard Worker def _getaddr(self, arg): 449*cda5da8dSAndroid Build Coastguard Worker if not arg: 450*cda5da8dSAndroid Build Coastguard Worker return '', '' 451*cda5da8dSAndroid Build Coastguard Worker if arg.lstrip().startswith('<'): 452*cda5da8dSAndroid Build Coastguard Worker address, rest = get_angle_addr(arg) 453*cda5da8dSAndroid Build Coastguard Worker else: 454*cda5da8dSAndroid Build Coastguard Worker address, rest = get_addr_spec(arg) 455*cda5da8dSAndroid Build Coastguard Worker if not address: 456*cda5da8dSAndroid Build Coastguard Worker return address, rest 457*cda5da8dSAndroid Build Coastguard Worker return address.addr_spec, rest 458*cda5da8dSAndroid Build Coastguard Worker 459*cda5da8dSAndroid Build Coastguard Worker def _getparams(self, params): 460*cda5da8dSAndroid Build Coastguard Worker # Return params as dictionary. Return None if not all parameters 461*cda5da8dSAndroid Build Coastguard Worker # appear to be syntactically valid according to RFC 1869. 462*cda5da8dSAndroid Build Coastguard Worker result = {} 463*cda5da8dSAndroid Build Coastguard Worker for param in params: 464*cda5da8dSAndroid Build Coastguard Worker param, eq, value = param.partition('=') 465*cda5da8dSAndroid Build Coastguard Worker if not param.isalnum() or eq and not value: 466*cda5da8dSAndroid Build Coastguard Worker return None 467*cda5da8dSAndroid Build Coastguard Worker result[param] = value if eq else True 468*cda5da8dSAndroid Build Coastguard Worker return result 469*cda5da8dSAndroid Build Coastguard Worker 470*cda5da8dSAndroid Build Coastguard Worker def smtp_HELP(self, arg): 471*cda5da8dSAndroid Build Coastguard Worker if arg: 472*cda5da8dSAndroid Build Coastguard Worker extended = ' [SP <mail-parameters>]' 473*cda5da8dSAndroid Build Coastguard Worker lc_arg = arg.upper() 474*cda5da8dSAndroid Build Coastguard Worker if lc_arg == 'EHLO': 475*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: EHLO hostname') 476*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'HELO': 477*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: HELO hostname') 478*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'MAIL': 479*cda5da8dSAndroid Build Coastguard Worker msg = '250 Syntax: MAIL FROM: <address>' 480*cda5da8dSAndroid Build Coastguard Worker if self.extended_smtp: 481*cda5da8dSAndroid Build Coastguard Worker msg += extended 482*cda5da8dSAndroid Build Coastguard Worker self.push(msg) 483*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'RCPT': 484*cda5da8dSAndroid Build Coastguard Worker msg = '250 Syntax: RCPT TO: <address>' 485*cda5da8dSAndroid Build Coastguard Worker if self.extended_smtp: 486*cda5da8dSAndroid Build Coastguard Worker msg += extended 487*cda5da8dSAndroid Build Coastguard Worker self.push(msg) 488*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'DATA': 489*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: DATA') 490*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'RSET': 491*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: RSET') 492*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'NOOP': 493*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: NOOP') 494*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'QUIT': 495*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: QUIT') 496*cda5da8dSAndroid Build Coastguard Worker elif lc_arg == 'VRFY': 497*cda5da8dSAndroid Build Coastguard Worker self.push('250 Syntax: VRFY <address>') 498*cda5da8dSAndroid Build Coastguard Worker else: 499*cda5da8dSAndroid Build Coastguard Worker self.push('501 Supported commands: EHLO HELO MAIL RCPT ' 500*cda5da8dSAndroid Build Coastguard Worker 'DATA RSET NOOP QUIT VRFY') 501*cda5da8dSAndroid Build Coastguard Worker else: 502*cda5da8dSAndroid Build Coastguard Worker self.push('250 Supported commands: EHLO HELO MAIL RCPT DATA ' 503*cda5da8dSAndroid Build Coastguard Worker 'RSET NOOP QUIT VRFY') 504*cda5da8dSAndroid Build Coastguard Worker 505*cda5da8dSAndroid Build Coastguard Worker def smtp_VRFY(self, arg): 506*cda5da8dSAndroid Build Coastguard Worker if arg: 507*cda5da8dSAndroid Build Coastguard Worker address, params = self._getaddr(arg) 508*cda5da8dSAndroid Build Coastguard Worker if address: 509*cda5da8dSAndroid Build Coastguard Worker self.push('252 Cannot VRFY user, but will accept message ' 510*cda5da8dSAndroid Build Coastguard Worker 'and attempt delivery') 511*cda5da8dSAndroid Build Coastguard Worker else: 512*cda5da8dSAndroid Build Coastguard Worker self.push('502 Could not VRFY %s' % arg) 513*cda5da8dSAndroid Build Coastguard Worker else: 514*cda5da8dSAndroid Build Coastguard Worker self.push('501 Syntax: VRFY <address>') 515*cda5da8dSAndroid Build Coastguard Worker 516*cda5da8dSAndroid Build Coastguard Worker def smtp_MAIL(self, arg): 517*cda5da8dSAndroid Build Coastguard Worker if not self.seen_greeting: 518*cda5da8dSAndroid Build Coastguard Worker self.push('503 Error: send HELO first') 519*cda5da8dSAndroid Build Coastguard Worker return 520*cda5da8dSAndroid Build Coastguard Worker print('===> MAIL', arg, file=DEBUGSTREAM) 521*cda5da8dSAndroid Build Coastguard Worker syntaxerr = '501 Syntax: MAIL FROM: <address>' 522*cda5da8dSAndroid Build Coastguard Worker if self.extended_smtp: 523*cda5da8dSAndroid Build Coastguard Worker syntaxerr += ' [SP <mail-parameters>]' 524*cda5da8dSAndroid Build Coastguard Worker if arg is None: 525*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 526*cda5da8dSAndroid Build Coastguard Worker return 527*cda5da8dSAndroid Build Coastguard Worker arg = self._strip_command_keyword('FROM:', arg) 528*cda5da8dSAndroid Build Coastguard Worker address, params = self._getaddr(arg) 529*cda5da8dSAndroid Build Coastguard Worker if not address: 530*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 531*cda5da8dSAndroid Build Coastguard Worker return 532*cda5da8dSAndroid Build Coastguard Worker if not self.extended_smtp and params: 533*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 534*cda5da8dSAndroid Build Coastguard Worker return 535*cda5da8dSAndroid Build Coastguard Worker if self.mailfrom: 536*cda5da8dSAndroid Build Coastguard Worker self.push('503 Error: nested MAIL command') 537*cda5da8dSAndroid Build Coastguard Worker return 538*cda5da8dSAndroid Build Coastguard Worker self.mail_options = params.upper().split() 539*cda5da8dSAndroid Build Coastguard Worker params = self._getparams(self.mail_options) 540*cda5da8dSAndroid Build Coastguard Worker if params is None: 541*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 542*cda5da8dSAndroid Build Coastguard Worker return 543*cda5da8dSAndroid Build Coastguard Worker if not self._decode_data: 544*cda5da8dSAndroid Build Coastguard Worker body = params.pop('BODY', '7BIT') 545*cda5da8dSAndroid Build Coastguard Worker if body not in ['7BIT', '8BITMIME']: 546*cda5da8dSAndroid Build Coastguard Worker self.push('501 Error: BODY can only be one of 7BIT, 8BITMIME') 547*cda5da8dSAndroid Build Coastguard Worker return 548*cda5da8dSAndroid Build Coastguard Worker if self.enable_SMTPUTF8: 549*cda5da8dSAndroid Build Coastguard Worker smtputf8 = params.pop('SMTPUTF8', False) 550*cda5da8dSAndroid Build Coastguard Worker if smtputf8 is True: 551*cda5da8dSAndroid Build Coastguard Worker self.require_SMTPUTF8 = True 552*cda5da8dSAndroid Build Coastguard Worker elif smtputf8 is not False: 553*cda5da8dSAndroid Build Coastguard Worker self.push('501 Error: SMTPUTF8 takes no arguments') 554*cda5da8dSAndroid Build Coastguard Worker return 555*cda5da8dSAndroid Build Coastguard Worker size = params.pop('SIZE', None) 556*cda5da8dSAndroid Build Coastguard Worker if size: 557*cda5da8dSAndroid Build Coastguard Worker if not size.isdigit(): 558*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 559*cda5da8dSAndroid Build Coastguard Worker return 560*cda5da8dSAndroid Build Coastguard Worker elif self.data_size_limit and int(size) > self.data_size_limit: 561*cda5da8dSAndroid Build Coastguard Worker self.push('552 Error: message size exceeds fixed maximum message size') 562*cda5da8dSAndroid Build Coastguard Worker return 563*cda5da8dSAndroid Build Coastguard Worker if len(params.keys()) > 0: 564*cda5da8dSAndroid Build Coastguard Worker self.push('555 MAIL FROM parameters not recognized or not implemented') 565*cda5da8dSAndroid Build Coastguard Worker return 566*cda5da8dSAndroid Build Coastguard Worker self.mailfrom = address 567*cda5da8dSAndroid Build Coastguard Worker print('sender:', self.mailfrom, file=DEBUGSTREAM) 568*cda5da8dSAndroid Build Coastguard Worker self.push('250 OK') 569*cda5da8dSAndroid Build Coastguard Worker 570*cda5da8dSAndroid Build Coastguard Worker def smtp_RCPT(self, arg): 571*cda5da8dSAndroid Build Coastguard Worker if not self.seen_greeting: 572*cda5da8dSAndroid Build Coastguard Worker self.push('503 Error: send HELO first'); 573*cda5da8dSAndroid Build Coastguard Worker return 574*cda5da8dSAndroid Build Coastguard Worker print('===> RCPT', arg, file=DEBUGSTREAM) 575*cda5da8dSAndroid Build Coastguard Worker if not self.mailfrom: 576*cda5da8dSAndroid Build Coastguard Worker self.push('503 Error: need MAIL command') 577*cda5da8dSAndroid Build Coastguard Worker return 578*cda5da8dSAndroid Build Coastguard Worker syntaxerr = '501 Syntax: RCPT TO: <address>' 579*cda5da8dSAndroid Build Coastguard Worker if self.extended_smtp: 580*cda5da8dSAndroid Build Coastguard Worker syntaxerr += ' [SP <mail-parameters>]' 581*cda5da8dSAndroid Build Coastguard Worker if arg is None: 582*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 583*cda5da8dSAndroid Build Coastguard Worker return 584*cda5da8dSAndroid Build Coastguard Worker arg = self._strip_command_keyword('TO:', arg) 585*cda5da8dSAndroid Build Coastguard Worker address, params = self._getaddr(arg) 586*cda5da8dSAndroid Build Coastguard Worker if not address: 587*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 588*cda5da8dSAndroid Build Coastguard Worker return 589*cda5da8dSAndroid Build Coastguard Worker if not self.extended_smtp and params: 590*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 591*cda5da8dSAndroid Build Coastguard Worker return 592*cda5da8dSAndroid Build Coastguard Worker self.rcpt_options = params.upper().split() 593*cda5da8dSAndroid Build Coastguard Worker params = self._getparams(self.rcpt_options) 594*cda5da8dSAndroid Build Coastguard Worker if params is None: 595*cda5da8dSAndroid Build Coastguard Worker self.push(syntaxerr) 596*cda5da8dSAndroid Build Coastguard Worker return 597*cda5da8dSAndroid Build Coastguard Worker # XXX currently there are no options we recognize. 598*cda5da8dSAndroid Build Coastguard Worker if len(params.keys()) > 0: 599*cda5da8dSAndroid Build Coastguard Worker self.push('555 RCPT TO parameters not recognized or not implemented') 600*cda5da8dSAndroid Build Coastguard Worker return 601*cda5da8dSAndroid Build Coastguard Worker self.rcpttos.append(address) 602*cda5da8dSAndroid Build Coastguard Worker print('recips:', self.rcpttos, file=DEBUGSTREAM) 603*cda5da8dSAndroid Build Coastguard Worker self.push('250 OK') 604*cda5da8dSAndroid Build Coastguard Worker 605*cda5da8dSAndroid Build Coastguard Worker def smtp_RSET(self, arg): 606*cda5da8dSAndroid Build Coastguard Worker if arg: 607*cda5da8dSAndroid Build Coastguard Worker self.push('501 Syntax: RSET') 608*cda5da8dSAndroid Build Coastguard Worker return 609*cda5da8dSAndroid Build Coastguard Worker self._set_rset_state() 610*cda5da8dSAndroid Build Coastguard Worker self.push('250 OK') 611*cda5da8dSAndroid Build Coastguard Worker 612*cda5da8dSAndroid Build Coastguard Worker def smtp_DATA(self, arg): 613*cda5da8dSAndroid Build Coastguard Worker if not self.seen_greeting: 614*cda5da8dSAndroid Build Coastguard Worker self.push('503 Error: send HELO first'); 615*cda5da8dSAndroid Build Coastguard Worker return 616*cda5da8dSAndroid Build Coastguard Worker if not self.rcpttos: 617*cda5da8dSAndroid Build Coastguard Worker self.push('503 Error: need RCPT command') 618*cda5da8dSAndroid Build Coastguard Worker return 619*cda5da8dSAndroid Build Coastguard Worker if arg: 620*cda5da8dSAndroid Build Coastguard Worker self.push('501 Syntax: DATA') 621*cda5da8dSAndroid Build Coastguard Worker return 622*cda5da8dSAndroid Build Coastguard Worker self.smtp_state = self.DATA 623*cda5da8dSAndroid Build Coastguard Worker self.set_terminator(b'\r\n.\r\n') 624*cda5da8dSAndroid Build Coastguard Worker self.push('354 End data with <CR><LF>.<CR><LF>') 625*cda5da8dSAndroid Build Coastguard Worker 626*cda5da8dSAndroid Build Coastguard Worker # Commands that have not been implemented 627*cda5da8dSAndroid Build Coastguard Worker def smtp_EXPN(self, arg): 628*cda5da8dSAndroid Build Coastguard Worker self.push('502 EXPN not implemented') 629*cda5da8dSAndroid Build Coastguard Worker 630*cda5da8dSAndroid Build Coastguard Worker 631*cda5da8dSAndroid Build Coastguard Workerclass SMTPServer(asyncore.dispatcher): 632*cda5da8dSAndroid Build Coastguard Worker # SMTPChannel class to use for managing client connections 633*cda5da8dSAndroid Build Coastguard Worker channel_class = SMTPChannel 634*cda5da8dSAndroid Build Coastguard Worker 635*cda5da8dSAndroid Build Coastguard Worker def __init__(self, localaddr, remoteaddr, 636*cda5da8dSAndroid Build Coastguard Worker data_size_limit=DATA_SIZE_DEFAULT, map=None, 637*cda5da8dSAndroid Build Coastguard Worker enable_SMTPUTF8=False, decode_data=False): 638*cda5da8dSAndroid Build Coastguard Worker self._localaddr = localaddr 639*cda5da8dSAndroid Build Coastguard Worker self._remoteaddr = remoteaddr 640*cda5da8dSAndroid Build Coastguard Worker self.data_size_limit = data_size_limit 641*cda5da8dSAndroid Build Coastguard Worker self.enable_SMTPUTF8 = enable_SMTPUTF8 642*cda5da8dSAndroid Build Coastguard Worker self._decode_data = decode_data 643*cda5da8dSAndroid Build Coastguard Worker if enable_SMTPUTF8 and decode_data: 644*cda5da8dSAndroid Build Coastguard Worker raise ValueError("decode_data and enable_SMTPUTF8 cannot" 645*cda5da8dSAndroid Build Coastguard Worker " be set to True at the same time") 646*cda5da8dSAndroid Build Coastguard Worker asyncore.dispatcher.__init__(self, map=map) 647*cda5da8dSAndroid Build Coastguard Worker try: 648*cda5da8dSAndroid Build Coastguard Worker gai_results = socket.getaddrinfo(*localaddr, 649*cda5da8dSAndroid Build Coastguard Worker type=socket.SOCK_STREAM) 650*cda5da8dSAndroid Build Coastguard Worker self.create_socket(gai_results[0][0], gai_results[0][1]) 651*cda5da8dSAndroid Build Coastguard Worker # try to re-use a server port if possible 652*cda5da8dSAndroid Build Coastguard Worker self.set_reuse_addr() 653*cda5da8dSAndroid Build Coastguard Worker self.bind(localaddr) 654*cda5da8dSAndroid Build Coastguard Worker self.listen(5) 655*cda5da8dSAndroid Build Coastguard Worker except: 656*cda5da8dSAndroid Build Coastguard Worker self.close() 657*cda5da8dSAndroid Build Coastguard Worker raise 658*cda5da8dSAndroid Build Coastguard Worker else: 659*cda5da8dSAndroid Build Coastguard Worker print('%s started at %s\n\tLocal addr: %s\n\tRemote addr:%s' % ( 660*cda5da8dSAndroid Build Coastguard Worker self.__class__.__name__, time.ctime(time.time()), 661*cda5da8dSAndroid Build Coastguard Worker localaddr, remoteaddr), file=DEBUGSTREAM) 662*cda5da8dSAndroid Build Coastguard Worker 663*cda5da8dSAndroid Build Coastguard Worker def handle_accepted(self, conn, addr): 664*cda5da8dSAndroid Build Coastguard Worker print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM) 665*cda5da8dSAndroid Build Coastguard Worker channel = self.channel_class(self, 666*cda5da8dSAndroid Build Coastguard Worker conn, 667*cda5da8dSAndroid Build Coastguard Worker addr, 668*cda5da8dSAndroid Build Coastguard Worker self.data_size_limit, 669*cda5da8dSAndroid Build Coastguard Worker self._map, 670*cda5da8dSAndroid Build Coastguard Worker self.enable_SMTPUTF8, 671*cda5da8dSAndroid Build Coastguard Worker self._decode_data) 672*cda5da8dSAndroid Build Coastguard Worker 673*cda5da8dSAndroid Build Coastguard Worker # API for "doing something useful with the message" 674*cda5da8dSAndroid Build Coastguard Worker def process_message(self, peer, mailfrom, rcpttos, data, **kwargs): 675*cda5da8dSAndroid Build Coastguard Worker """Override this abstract method to handle messages from the client. 676*cda5da8dSAndroid Build Coastguard Worker 677*cda5da8dSAndroid Build Coastguard Worker peer is a tuple containing (ipaddr, port) of the client that made the 678*cda5da8dSAndroid Build Coastguard Worker socket connection to our smtp port. 679*cda5da8dSAndroid Build Coastguard Worker 680*cda5da8dSAndroid Build Coastguard Worker mailfrom is the raw address the client claims the message is coming 681*cda5da8dSAndroid Build Coastguard Worker from. 682*cda5da8dSAndroid Build Coastguard Worker 683*cda5da8dSAndroid Build Coastguard Worker rcpttos is a list of raw addresses the client wishes to deliver the 684*cda5da8dSAndroid Build Coastguard Worker message to. 685*cda5da8dSAndroid Build Coastguard Worker 686*cda5da8dSAndroid Build Coastguard Worker data is a string containing the entire full text of the message, 687*cda5da8dSAndroid Build Coastguard Worker headers (if supplied) and all. It has been `de-transparencied' 688*cda5da8dSAndroid Build Coastguard Worker according to RFC 821, Section 4.5.2. In other words, a line 689*cda5da8dSAndroid Build Coastguard Worker containing a `.' followed by other text has had the leading dot 690*cda5da8dSAndroid Build Coastguard Worker removed. 691*cda5da8dSAndroid Build Coastguard Worker 692*cda5da8dSAndroid Build Coastguard Worker kwargs is a dictionary containing additional information. It is 693*cda5da8dSAndroid Build Coastguard Worker empty if decode_data=True was given as init parameter, otherwise 694*cda5da8dSAndroid Build Coastguard Worker it will contain the following keys: 695*cda5da8dSAndroid Build Coastguard Worker 'mail_options': list of parameters to the mail command. All 696*cda5da8dSAndroid Build Coastguard Worker elements are uppercase strings. Example: 697*cda5da8dSAndroid Build Coastguard Worker ['BODY=8BITMIME', 'SMTPUTF8']. 698*cda5da8dSAndroid Build Coastguard Worker 'rcpt_options': same, for the rcpt command. 699*cda5da8dSAndroid Build Coastguard Worker 700*cda5da8dSAndroid Build Coastguard Worker This function should return None for a normal `250 Ok' response; 701*cda5da8dSAndroid Build Coastguard Worker otherwise, it should return the desired response string in RFC 821 702*cda5da8dSAndroid Build Coastguard Worker format. 703*cda5da8dSAndroid Build Coastguard Worker 704*cda5da8dSAndroid Build Coastguard Worker """ 705*cda5da8dSAndroid Build Coastguard Worker raise NotImplementedError 706*cda5da8dSAndroid Build Coastguard Worker 707*cda5da8dSAndroid Build Coastguard Worker 708*cda5da8dSAndroid Build Coastguard Workerclass DebuggingServer(SMTPServer): 709*cda5da8dSAndroid Build Coastguard Worker 710*cda5da8dSAndroid Build Coastguard Worker def _print_message_content(self, peer, data): 711*cda5da8dSAndroid Build Coastguard Worker inheaders = 1 712*cda5da8dSAndroid Build Coastguard Worker lines = data.splitlines() 713*cda5da8dSAndroid Build Coastguard Worker for line in lines: 714*cda5da8dSAndroid Build Coastguard Worker # headers first 715*cda5da8dSAndroid Build Coastguard Worker if inheaders and not line: 716*cda5da8dSAndroid Build Coastguard Worker peerheader = 'X-Peer: ' + peer[0] 717*cda5da8dSAndroid Build Coastguard Worker if not isinstance(data, str): 718*cda5da8dSAndroid Build Coastguard Worker # decoded_data=false; make header match other binary output 719*cda5da8dSAndroid Build Coastguard Worker peerheader = repr(peerheader.encode('utf-8')) 720*cda5da8dSAndroid Build Coastguard Worker print(peerheader) 721*cda5da8dSAndroid Build Coastguard Worker inheaders = 0 722*cda5da8dSAndroid Build Coastguard Worker if not isinstance(data, str): 723*cda5da8dSAndroid Build Coastguard Worker # Avoid spurious 'str on bytes instance' warning. 724*cda5da8dSAndroid Build Coastguard Worker line = repr(line) 725*cda5da8dSAndroid Build Coastguard Worker print(line) 726*cda5da8dSAndroid Build Coastguard Worker 727*cda5da8dSAndroid Build Coastguard Worker def process_message(self, peer, mailfrom, rcpttos, data, **kwargs): 728*cda5da8dSAndroid Build Coastguard Worker print('---------- MESSAGE FOLLOWS ----------') 729*cda5da8dSAndroid Build Coastguard Worker if kwargs: 730*cda5da8dSAndroid Build Coastguard Worker if kwargs.get('mail_options'): 731*cda5da8dSAndroid Build Coastguard Worker print('mail options: %s' % kwargs['mail_options']) 732*cda5da8dSAndroid Build Coastguard Worker if kwargs.get('rcpt_options'): 733*cda5da8dSAndroid Build Coastguard Worker print('rcpt options: %s\n' % kwargs['rcpt_options']) 734*cda5da8dSAndroid Build Coastguard Worker self._print_message_content(peer, data) 735*cda5da8dSAndroid Build Coastguard Worker print('------------ END MESSAGE ------------') 736*cda5da8dSAndroid Build Coastguard Worker 737*cda5da8dSAndroid Build Coastguard Worker 738*cda5da8dSAndroid Build Coastguard Workerclass PureProxy(SMTPServer): 739*cda5da8dSAndroid Build Coastguard Worker def __init__(self, *args, **kwargs): 740*cda5da8dSAndroid Build Coastguard Worker if 'enable_SMTPUTF8' in kwargs and kwargs['enable_SMTPUTF8']: 741*cda5da8dSAndroid Build Coastguard Worker raise ValueError("PureProxy does not support SMTPUTF8.") 742*cda5da8dSAndroid Build Coastguard Worker super(PureProxy, self).__init__(*args, **kwargs) 743*cda5da8dSAndroid Build Coastguard Worker 744*cda5da8dSAndroid Build Coastguard Worker def process_message(self, peer, mailfrom, rcpttos, data): 745*cda5da8dSAndroid Build Coastguard Worker lines = data.split('\n') 746*cda5da8dSAndroid Build Coastguard Worker # Look for the last header 747*cda5da8dSAndroid Build Coastguard Worker i = 0 748*cda5da8dSAndroid Build Coastguard Worker for line in lines: 749*cda5da8dSAndroid Build Coastguard Worker if not line: 750*cda5da8dSAndroid Build Coastguard Worker break 751*cda5da8dSAndroid Build Coastguard Worker i += 1 752*cda5da8dSAndroid Build Coastguard Worker lines.insert(i, 'X-Peer: %s' % peer[0]) 753*cda5da8dSAndroid Build Coastguard Worker data = NEWLINE.join(lines) 754*cda5da8dSAndroid Build Coastguard Worker refused = self._deliver(mailfrom, rcpttos, data) 755*cda5da8dSAndroid Build Coastguard Worker # TBD: what to do with refused addresses? 756*cda5da8dSAndroid Build Coastguard Worker print('we got some refusals:', refused, file=DEBUGSTREAM) 757*cda5da8dSAndroid Build Coastguard Worker 758*cda5da8dSAndroid Build Coastguard Worker def _deliver(self, mailfrom, rcpttos, data): 759*cda5da8dSAndroid Build Coastguard Worker import smtplib 760*cda5da8dSAndroid Build Coastguard Worker refused = {} 761*cda5da8dSAndroid Build Coastguard Worker try: 762*cda5da8dSAndroid Build Coastguard Worker s = smtplib.SMTP() 763*cda5da8dSAndroid Build Coastguard Worker s.connect(self._remoteaddr[0], self._remoteaddr[1]) 764*cda5da8dSAndroid Build Coastguard Worker try: 765*cda5da8dSAndroid Build Coastguard Worker refused = s.sendmail(mailfrom, rcpttos, data) 766*cda5da8dSAndroid Build Coastguard Worker finally: 767*cda5da8dSAndroid Build Coastguard Worker s.quit() 768*cda5da8dSAndroid Build Coastguard Worker except smtplib.SMTPRecipientsRefused as e: 769*cda5da8dSAndroid Build Coastguard Worker print('got SMTPRecipientsRefused', file=DEBUGSTREAM) 770*cda5da8dSAndroid Build Coastguard Worker refused = e.recipients 771*cda5da8dSAndroid Build Coastguard Worker except (OSError, smtplib.SMTPException) as e: 772*cda5da8dSAndroid Build Coastguard Worker print('got', e.__class__, file=DEBUGSTREAM) 773*cda5da8dSAndroid Build Coastguard Worker # All recipients were refused. If the exception had an associated 774*cda5da8dSAndroid Build Coastguard Worker # error code, use it. Otherwise,fake it with a non-triggering 775*cda5da8dSAndroid Build Coastguard Worker # exception code. 776*cda5da8dSAndroid Build Coastguard Worker errcode = getattr(e, 'smtp_code', -1) 777*cda5da8dSAndroid Build Coastguard Worker errmsg = getattr(e, 'smtp_error', 'ignore') 778*cda5da8dSAndroid Build Coastguard Worker for r in rcpttos: 779*cda5da8dSAndroid Build Coastguard Worker refused[r] = (errcode, errmsg) 780*cda5da8dSAndroid Build Coastguard Worker return refused 781*cda5da8dSAndroid Build Coastguard Worker 782*cda5da8dSAndroid Build Coastguard Worker 783*cda5da8dSAndroid Build Coastguard Workerclass Options: 784*cda5da8dSAndroid Build Coastguard Worker setuid = True 785*cda5da8dSAndroid Build Coastguard Worker classname = 'PureProxy' 786*cda5da8dSAndroid Build Coastguard Worker size_limit = None 787*cda5da8dSAndroid Build Coastguard Worker enable_SMTPUTF8 = False 788*cda5da8dSAndroid Build Coastguard Worker 789*cda5da8dSAndroid Build Coastguard Worker 790*cda5da8dSAndroid Build Coastguard Workerdef parseargs(): 791*cda5da8dSAndroid Build Coastguard Worker global DEBUGSTREAM 792*cda5da8dSAndroid Build Coastguard Worker try: 793*cda5da8dSAndroid Build Coastguard Worker opts, args = getopt.getopt( 794*cda5da8dSAndroid Build Coastguard Worker sys.argv[1:], 'nVhc:s:du', 795*cda5da8dSAndroid Build Coastguard Worker ['class=', 'nosetuid', 'version', 'help', 'size=', 'debug', 796*cda5da8dSAndroid Build Coastguard Worker 'smtputf8']) 797*cda5da8dSAndroid Build Coastguard Worker except getopt.error as e: 798*cda5da8dSAndroid Build Coastguard Worker usage(1, e) 799*cda5da8dSAndroid Build Coastguard Worker 800*cda5da8dSAndroid Build Coastguard Worker options = Options() 801*cda5da8dSAndroid Build Coastguard Worker for opt, arg in opts: 802*cda5da8dSAndroid Build Coastguard Worker if opt in ('-h', '--help'): 803*cda5da8dSAndroid Build Coastguard Worker usage(0) 804*cda5da8dSAndroid Build Coastguard Worker elif opt in ('-V', '--version'): 805*cda5da8dSAndroid Build Coastguard Worker print(__version__) 806*cda5da8dSAndroid Build Coastguard Worker sys.exit(0) 807*cda5da8dSAndroid Build Coastguard Worker elif opt in ('-n', '--nosetuid'): 808*cda5da8dSAndroid Build Coastguard Worker options.setuid = False 809*cda5da8dSAndroid Build Coastguard Worker elif opt in ('-c', '--class'): 810*cda5da8dSAndroid Build Coastguard Worker options.classname = arg 811*cda5da8dSAndroid Build Coastguard Worker elif opt in ('-d', '--debug'): 812*cda5da8dSAndroid Build Coastguard Worker DEBUGSTREAM = sys.stderr 813*cda5da8dSAndroid Build Coastguard Worker elif opt in ('-u', '--smtputf8'): 814*cda5da8dSAndroid Build Coastguard Worker options.enable_SMTPUTF8 = True 815*cda5da8dSAndroid Build Coastguard Worker elif opt in ('-s', '--size'): 816*cda5da8dSAndroid Build Coastguard Worker try: 817*cda5da8dSAndroid Build Coastguard Worker int_size = int(arg) 818*cda5da8dSAndroid Build Coastguard Worker options.size_limit = int_size 819*cda5da8dSAndroid Build Coastguard Worker except: 820*cda5da8dSAndroid Build Coastguard Worker print('Invalid size: ' + arg, file=sys.stderr) 821*cda5da8dSAndroid Build Coastguard Worker sys.exit(1) 822*cda5da8dSAndroid Build Coastguard Worker 823*cda5da8dSAndroid Build Coastguard Worker # parse the rest of the arguments 824*cda5da8dSAndroid Build Coastguard Worker if len(args) < 1: 825*cda5da8dSAndroid Build Coastguard Worker localspec = 'localhost:8025' 826*cda5da8dSAndroid Build Coastguard Worker remotespec = 'localhost:25' 827*cda5da8dSAndroid Build Coastguard Worker elif len(args) < 2: 828*cda5da8dSAndroid Build Coastguard Worker localspec = args[0] 829*cda5da8dSAndroid Build Coastguard Worker remotespec = 'localhost:25' 830*cda5da8dSAndroid Build Coastguard Worker elif len(args) < 3: 831*cda5da8dSAndroid Build Coastguard Worker localspec = args[0] 832*cda5da8dSAndroid Build Coastguard Worker remotespec = args[1] 833*cda5da8dSAndroid Build Coastguard Worker else: 834*cda5da8dSAndroid Build Coastguard Worker usage(1, 'Invalid arguments: %s' % COMMASPACE.join(args)) 835*cda5da8dSAndroid Build Coastguard Worker 836*cda5da8dSAndroid Build Coastguard Worker # split into host/port pairs 837*cda5da8dSAndroid Build Coastguard Worker i = localspec.find(':') 838*cda5da8dSAndroid Build Coastguard Worker if i < 0: 839*cda5da8dSAndroid Build Coastguard Worker usage(1, 'Bad local spec: %s' % localspec) 840*cda5da8dSAndroid Build Coastguard Worker options.localhost = localspec[:i] 841*cda5da8dSAndroid Build Coastguard Worker try: 842*cda5da8dSAndroid Build Coastguard Worker options.localport = int(localspec[i+1:]) 843*cda5da8dSAndroid Build Coastguard Worker except ValueError: 844*cda5da8dSAndroid Build Coastguard Worker usage(1, 'Bad local port: %s' % localspec) 845*cda5da8dSAndroid Build Coastguard Worker i = remotespec.find(':') 846*cda5da8dSAndroid Build Coastguard Worker if i < 0: 847*cda5da8dSAndroid Build Coastguard Worker usage(1, 'Bad remote spec: %s' % remotespec) 848*cda5da8dSAndroid Build Coastguard Worker options.remotehost = remotespec[:i] 849*cda5da8dSAndroid Build Coastguard Worker try: 850*cda5da8dSAndroid Build Coastguard Worker options.remoteport = int(remotespec[i+1:]) 851*cda5da8dSAndroid Build Coastguard Worker except ValueError: 852*cda5da8dSAndroid Build Coastguard Worker usage(1, 'Bad remote port: %s' % remotespec) 853*cda5da8dSAndroid Build Coastguard Worker return options 854*cda5da8dSAndroid Build Coastguard Worker 855*cda5da8dSAndroid Build Coastguard Worker 856*cda5da8dSAndroid Build Coastguard Workerif __name__ == '__main__': 857*cda5da8dSAndroid Build Coastguard Worker options = parseargs() 858*cda5da8dSAndroid Build Coastguard Worker # Become nobody 859*cda5da8dSAndroid Build Coastguard Worker classname = options.classname 860*cda5da8dSAndroid Build Coastguard Worker if "." in classname: 861*cda5da8dSAndroid Build Coastguard Worker lastdot = classname.rfind(".") 862*cda5da8dSAndroid Build Coastguard Worker mod = __import__(classname[:lastdot], globals(), locals(), [""]) 863*cda5da8dSAndroid Build Coastguard Worker classname = classname[lastdot+1:] 864*cda5da8dSAndroid Build Coastguard Worker else: 865*cda5da8dSAndroid Build Coastguard Worker import __main__ as mod 866*cda5da8dSAndroid Build Coastguard Worker class_ = getattr(mod, classname) 867*cda5da8dSAndroid Build Coastguard Worker proxy = class_((options.localhost, options.localport), 868*cda5da8dSAndroid Build Coastguard Worker (options.remotehost, options.remoteport), 869*cda5da8dSAndroid Build Coastguard Worker options.size_limit, enable_SMTPUTF8=options.enable_SMTPUTF8) 870*cda5da8dSAndroid Build Coastguard Worker if options.setuid: 871*cda5da8dSAndroid Build Coastguard Worker try: 872*cda5da8dSAndroid Build Coastguard Worker import pwd 873*cda5da8dSAndroid Build Coastguard Worker except ImportError: 874*cda5da8dSAndroid Build Coastguard Worker print('Cannot import module "pwd"; try running with -n option.', file=sys.stderr) 875*cda5da8dSAndroid Build Coastguard Worker sys.exit(1) 876*cda5da8dSAndroid Build Coastguard Worker nobody = pwd.getpwnam('nobody')[2] 877*cda5da8dSAndroid Build Coastguard Worker try: 878*cda5da8dSAndroid Build Coastguard Worker os.setuid(nobody) 879*cda5da8dSAndroid Build Coastguard Worker except PermissionError: 880*cda5da8dSAndroid Build Coastguard Worker print('Cannot setuid "nobody"; try running with -n option.', file=sys.stderr) 881*cda5da8dSAndroid Build Coastguard Worker sys.exit(1) 882*cda5da8dSAndroid Build Coastguard Worker try: 883*cda5da8dSAndroid Build Coastguard Worker asyncore.loop() 884*cda5da8dSAndroid Build Coastguard Worker except KeyboardInterrupt: 885*cda5da8dSAndroid Build Coastguard Worker pass 886