Skip to content

Commit 6ea36d6

Browse files
committed
index on testlogging-noasyncore: a36235d pythongh-84461: Omit resource mod and getresuid funcs on Emscripten (pythonGH-96303)
1 parent a36235d commit 6ea36d6

File tree

1 file changed

+394
-0
lines changed

1 file changed

+394
-0
lines changed

Lib/test/support/server_helper.py

+394
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
1+
def _on_smpt_client(socket):
2+
"""A client processor for a test SMTP server."""
3+
socket.send(b'220 testhost Python SMTP proxy version 0.3\r\n')
4+
forced_exit = False
5+
while not forced_exit and (msg := socket.recv(1024)):
6+
ss
7+
8+
class SMTPChannel(asynchat.async_chat):
9+
COMMAND = 0
10+
DATA = 1
11+
12+
command_size_limit = 512
13+
command_size_limits = collections.defaultdict(lambda x=command_size_limit: x)
14+
15+
@property
16+
def max_command_size_limit(self):
17+
try:
18+
return max(self.command_size_limits.values())
19+
except ValueError:
20+
return self.command_size_limit
21+
22+
"""
23+
def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT,
24+
map=None, enable_SMTPUTF8=False, decode_data=False):
25+
asynchat.async_chat.__init__(self, conn, map=map)
26+
self.smtp_server = server
27+
self.conn = conn
28+
self.addr = addr
29+
self.data_size_limit = data_size_limit
30+
self.enable_SMTPUTF8 = enable_SMTPUTF8
31+
self._decode_data = decode_data
32+
if enable_SMTPUTF8 and decode_data:
33+
raise ValueError("decode_data and enable_SMTPUTF8 cannot"
34+
" be set to True at the same time")
35+
if decode_data:
36+
self._emptystring = ''
37+
self._linesep = '\r\n'
38+
self._dotsep = '.'
39+
self._newline = NEWLINE
40+
else:
41+
self._emptystring = b''
42+
self._linesep = b'\r\n'
43+
self._dotsep = ord(b'.')
44+
self._newline = b'\n'
45+
self._set_rset_state()
46+
self.seen_greeting = ''
47+
self.extended_smtp = False
48+
self.command_size_limits.clear()
49+
self.fqdn = socket.getfqdn()
50+
try:
51+
self.peer = conn.getpeername()
52+
except OSError as err:
53+
# a race condition may occur if the other end is closing
54+
# before we can get the peername
55+
self.close()
56+
if err.errno != errno.ENOTCONN:
57+
raise
58+
return
59+
print('Peer:', repr(self.peer), file=DEBUGSTREAM)
60+
self.push('220 %s %s' % (self.fqdn, __version__))
61+
"""
62+
63+
def _set_post_data_state(self):
64+
"""Reset state variables to their post-DATA state."""
65+
self.smtp_state = self.COMMAND
66+
self.mailfrom = None
67+
self.rcpttos = []
68+
self.require_SMTPUTF8 = False
69+
self.num_bytes = 0
70+
self.set_terminator(b'\r\n')
71+
72+
def _set_rset_state(self):
73+
"""Reset all state variables except the greeting."""
74+
self._set_post_data_state()
75+
self.received_data = ''
76+
self.received_lines = []
77+
78+
# Overrides base class for convenience.
79+
""" def push(self, msg):
80+
asynchat.async_chat.push(self, bytes(
81+
msg + '\r\n', 'utf-8' if self.require_SMTPUTF8 else 'ascii'))
82+
"""
83+
84+
# Implementation of base class abstract method
85+
def collect_incoming_data(self, data):
86+
limit = None
87+
if self.smtp_state == self.COMMAND:
88+
limit = self.max_command_size_limit
89+
elif self.smtp_state == self.DATA:
90+
limit = self.data_size_limit
91+
if limit and self.num_bytes > limit:
92+
return
93+
elif limit:
94+
self.num_bytes += len(data)
95+
if self._decode_data:
96+
self.received_lines.append(str(data, 'utf-8'))
97+
else:
98+
self.received_lines.append(data)
99+
100+
# Implementation of base class abstract method
101+
def found_terminator(self):
102+
line = self._emptystring.join(self.received_lines)
103+
print('Data:', repr(line), file=DEBUGSTREAM)
104+
self.received_lines = []
105+
if self.smtp_state == self.COMMAND:
106+
sz, self.num_bytes = self.num_bytes, 0
107+
if not line:
108+
self.push('500 Error: bad syntax')
109+
return
110+
if not self._decode_data:
111+
line = str(line, 'utf-8')
112+
i = line.find(' ')
113+
if i < 0:
114+
command = line.upper()
115+
arg = None
116+
else:
117+
command = line[:i].upper()
118+
arg = line[i+1:].strip()
119+
max_sz = (self.command_size_limits[command]
120+
if self.extended_smtp else self.command_size_limit)
121+
if sz > max_sz:
122+
self.push('500 Error: line too long')
123+
return
124+
method = getattr(self, 'smtp_' + command, None)
125+
if not method:
126+
self.push('500 Error: command "%s" not recognized' % command)
127+
return
128+
method(arg)
129+
return
130+
else:
131+
if self.smtp_state != self.DATA:
132+
self.push('451 Internal confusion')
133+
self.num_bytes = 0
134+
return
135+
if self.data_size_limit and self.num_bytes > self.data_size_limit:
136+
self.push('552 Error: Too much mail data')
137+
self.num_bytes = 0
138+
return
139+
# Remove extraneous carriage returns and de-transparency according
140+
# to RFC 5321, Section 4.5.2.
141+
data = []
142+
for text in line.split(self._linesep):
143+
if text and text[0] == self._dotsep:
144+
data.append(text[1:])
145+
else:
146+
data.append(text)
147+
self.received_data = self._newline.join(data)
148+
args = (self.peer, self.mailfrom, self.rcpttos, self.received_data)
149+
kwargs = {}
150+
if not self._decode_data:
151+
kwargs = {
152+
'mail_options': self.mail_options,
153+
'rcpt_options': self.rcpt_options,
154+
}
155+
status = self.smtp_server.process_message(*args, **kwargs)
156+
self._set_post_data_state()
157+
if not status:
158+
self.push('250 OK')
159+
else:
160+
self.push(status)
161+
162+
# SMTP and ESMTP commands
163+
def smtp_HELO(self, arg): ----------------------- переключение между состояниями. Возможно, пару "состояние + команда" стоит обединить в единую матрицу
164+
if not arg:
165+
self.push('501 Syntax: HELO hostname')
166+
return
167+
# See issue #21783 for a discussion of this behavior.
168+
if self.seen_greeting:
169+
self.push('503 Duplicate HELO/EHLO')
170+
return
171+
self._set_rset_state()
172+
self.seen_greeting = arg
173+
self.push('250 %s' % self.fqdn)
174+
175+
def smtp_EHLO(self, arg):
176+
if not arg:
177+
self.push('501 Syntax: EHLO hostname')
178+
return
179+
# See issue #21783 for a discussion of this behavior.
180+
if self.seen_greeting:
181+
self.push('503 Duplicate HELO/EHLO')
182+
return
183+
self._set_rset_state()
184+
self.seen_greeting = arg
185+
self.extended_smtp = True
186+
self.push('250-%s' % self.fqdn)
187+
if self.data_size_limit:
188+
self.push('250-SIZE %s' % self.data_size_limit)
189+
self.command_size_limits['MAIL'] += 26
190+
if not self._decode_data:
191+
self.push('250-8BITMIME')
192+
if self.enable_SMTPUTF8:
193+
self.push('250-SMTPUTF8')
194+
self.command_size_limits['MAIL'] += 10
195+
self.push('250 HELP')
196+
197+
def smtp_NOOP(self, arg):
198+
if arg:
199+
self.push('501 Syntax: NOOP')
200+
else:
201+
self.push('250 OK')
202+
203+
def smtp_QUIT(self, arg):
204+
# args is ignored
205+
self.push('221 Bye')
206+
self.close_when_done()
207+
208+
def _strip_command_keyword(self, keyword, arg):
209+
keylen = len(keyword)
210+
if arg[:keylen].upper() == keyword:
211+
return arg[keylen:].strip()
212+
return ''
213+
214+
def _getaddr(self, arg):
215+
if not arg:
216+
return '', ''
217+
if arg.lstrip().startswith('<'):
218+
address, rest = get_angle_addr(arg)
219+
else:
220+
address, rest = get_addr_spec(arg)
221+
if not address:
222+
return address, rest
223+
return address.addr_spec, rest
224+
225+
def _getparams(self, params):
226+
# Return params as dictionary. Return None if not all parameters
227+
# appear to be syntactically valid according to RFC 1869.
228+
result = {}
229+
for param in params:
230+
param, eq, value = param.partition('=')
231+
if not param.isalnum() or eq and not value:
232+
return None
233+
result[param] = value if eq else True
234+
return result
235+
236+
def smtp_HELP(self, arg):
237+
if arg:
238+
extended = ' [SP <mail-parameters>]'
239+
lc_arg = arg.upper()
240+
if lc_arg == 'EHLO':
241+
self.push('250 Syntax: EHLO hostname')
242+
elif lc_arg == 'HELO':
243+
self.push('250 Syntax: HELO hostname')
244+
elif lc_arg == 'MAIL':
245+
msg = '250 Syntax: MAIL FROM: <address>'
246+
if self.extended_smtp:
247+
msg += extended
248+
self.push(msg)
249+
elif lc_arg == 'RCPT':
250+
msg = '250 Syntax: RCPT TO: <address>'
251+
if self.extended_smtp:
252+
msg += extended
253+
self.push(msg)
254+
elif lc_arg == 'DATA':
255+
self.push('250 Syntax: DATA')
256+
elif lc_arg == 'RSET':
257+
self.push('250 Syntax: RSET')
258+
elif lc_arg == 'NOOP':
259+
self.push('250 Syntax: NOOP')
260+
elif lc_arg == 'QUIT':
261+
self.push('250 Syntax: QUIT')
262+
elif lc_arg == 'VRFY':
263+
self.push('250 Syntax: VRFY <address>')
264+
else:
265+
self.push('501 Supported commands: EHLO HELO MAIL RCPT '
266+
'DATA RSET NOOP QUIT VRFY')
267+
else:
268+
self.push('250 Supported commands: EHLO HELO MAIL RCPT DATA '
269+
'RSET NOOP QUIT VRFY')
270+
271+
def smtp_VRFY(self, arg):
272+
if arg:
273+
address, params = self._getaddr(arg)
274+
if address:
275+
self.push('252 Cannot VRFY user, but will accept message '
276+
'and attempt delivery')
277+
else:
278+
self.push('502 Could not VRFY %s' % arg)
279+
else:
280+
self.push('501 Syntax: VRFY <address>')
281+
282+
def smtp_MAIL(self, arg):
283+
if not self.seen_greeting:
284+
self.push('503 Error: send HELO first')
285+
return
286+
print('===> MAIL', arg, file=DEBUGSTREAM)
287+
syntaxerr = '501 Syntax: MAIL FROM: <address>'
288+
if self.extended_smtp:
289+
syntaxerr += ' [SP <mail-parameters>]'
290+
if arg is None:
291+
self.push(syntaxerr)
292+
return
293+
arg = self._strip_command_keyword('FROM:', arg)
294+
address, params = self._getaddr(arg)
295+
if not address:
296+
self.push(syntaxerr)
297+
return
298+
if not self.extended_smtp and params:
299+
self.push(syntaxerr)
300+
return
301+
if self.mailfrom:
302+
self.push('503 Error: nested MAIL command')
303+
return
304+
self.mail_options = params.upper().split()
305+
params = self._getparams(self.mail_options)
306+
if params is None:
307+
self.push(syntaxerr)
308+
return
309+
if not self._decode_data:
310+
body = params.pop('BODY', '7BIT')
311+
if body not in ['7BIT', '8BITMIME']:
312+
self.push('501 Error: BODY can only be one of 7BIT, 8BITMIME')
313+
return
314+
if self.enable_SMTPUTF8:
315+
smtputf8 = params.pop('SMTPUTF8', False)
316+
if smtputf8 is True:
317+
self.require_SMTPUTF8 = True
318+
elif smtputf8 is not False:
319+
self.push('501 Error: SMTPUTF8 takes no arguments')
320+
return
321+
size = params.pop('SIZE', None)
322+
if size:
323+
if not size.isdigit():
324+
self.push(syntaxerr)
325+
return
326+
elif self.data_size_limit and int(size) > self.data_size_limit:
327+
self.push('552 Error: message size exceeds fixed maximum message size')
328+
return
329+
if len(params.keys()) > 0:
330+
self.push('555 MAIL FROM parameters not recognized or not implemented')
331+
return
332+
self.mailfrom = address
333+
print('sender:', self.mailfrom, file=DEBUGSTREAM)
334+
self.push('250 OK')
335+
336+
def smtp_RCPT(self, arg):
337+
if not self.seen_greeting:
338+
self.push('503 Error: send HELO first');
339+
return
340+
print('===> RCPT', arg, file=DEBUGSTREAM)
341+
if not self.mailfrom:
342+
self.push('503 Error: need MAIL command')
343+
return
344+
syntaxerr = '501 Syntax: RCPT TO: <address>'
345+
if self.extended_smtp:
346+
syntaxerr += ' [SP <mail-parameters>]'
347+
if arg is None:
348+
self.push(syntaxerr)
349+
return
350+
arg = self._strip_command_keyword('TO:', arg)
351+
address, params = self._getaddr(arg)
352+
if not address:
353+
self.push(syntaxerr)
354+
return
355+
if not self.extended_smtp and params:
356+
self.push(syntaxerr)
357+
return
358+
self.rcpt_options = params.upper().split()
359+
params = self._getparams(self.rcpt_options)
360+
if params is None:
361+
self.push(syntaxerr)
362+
return
363+
# XXX currently there are no options we recognize.
364+
if len(params.keys()) > 0:
365+
self.push('555 RCPT TO parameters not recognized or not implemented')
366+
return
367+
self.rcpttos.append(address)
368+
print('recips:', self.rcpttos, file=DEBUGSTREAM)
369+
self.push('250 OK')
370+
371+
def smtp_RSET(self, arg):
372+
if arg:
373+
self.push('501 Syntax: RSET')
374+
return
375+
self._set_rset_state()
376+
self.push('250 OK')
377+
378+
def smtp_DATA(self, arg):
379+
if not self.seen_greeting:
380+
self.push('503 Error: send HELO first');
381+
return
382+
if not self.rcpttos:
383+
self.push('503 Error: need RCPT command')
384+
return
385+
if arg:
386+
self.push('501 Syntax: DATA')
387+
return
388+
self.smtp_state = self.DATA
389+
self.set_terminator(b'\r\n.\r\n')
390+
self.push('354 End data with <CR><LF>.<CR><LF>')
391+
392+
# Commands that have not been implemented
393+
def smtp_EXPN(self, arg):
394+
self.push('502 EXPN not implemented')

0 commit comments

Comments
 (0)