##// END OF EJS Templates
tests: force `dummysmtpd.py` to write its log file with '\n' on Windows...
Matt Harbison -
r52986:dbd2d562 default
parent child Browse files
Show More
@@ -1,161 +1,169 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 """dummy SMTP server for use in tests"""
3 """dummy SMTP server for use in tests"""
4
4
5
5
6 import io
6 import optparse
7 import optparse
7 import os
8 import os
8 import socket
9 import socket
9 import ssl
10 import ssl
10 import sys
11 import sys
11
12
12 from mercurial import (
13 from mercurial import (
13 pycompat,
14 pycompat,
14 server,
15 server,
15 sslutil,
16 sslutil,
16 ui as uimod,
17 ui as uimod,
17 )
18 )
18
19
20 if pycompat.iswindows:
21 sys.stdout = io.TextIOWrapper(
22 sys.stdout.buffer,
23 sys.stdout.encoding,
24 sys.stdout.errors,
25 newline="\n",
26 )
19
27
20 if os.environ.get('HGIPV6', '0') == '1':
28 if os.environ.get('HGIPV6', '0') == '1':
21 family = socket.AF_INET6
29 family = socket.AF_INET6
22 else:
30 else:
23 family = socket.AF_INET
31 family = socket.AF_INET
24
32
25
33
26 def log(msg):
34 def log(msg):
27 sys.stdout.write(msg)
35 sys.stdout.write(msg)
28 sys.stdout.flush()
36 sys.stdout.flush()
29
37
30
38
31 def mocksmtpserversession(conn, addr):
39 def mocksmtpserversession(conn, addr):
32 conn.send(b'220 smtp.example.com ESMTP\r\n')
40 conn.send(b'220 smtp.example.com ESMTP\r\n')
33
41
34 try:
42 try:
35 # Newer versions of OpenSSL raise on EOF
43 # Newer versions of OpenSSL raise on EOF
36 line = conn.recv(1024)
44 line = conn.recv(1024)
37 except ssl.SSLError:
45 except ssl.SSLError:
38 log('no hello: EOF\n')
46 log('no hello: EOF\n')
39 return
47 return
40
48
41 if not line.lower().startswith(b'ehlo '):
49 if not line.lower().startswith(b'ehlo '):
42 # Older versions of OpenSSl don't raise
50 # Older versions of OpenSSl don't raise
43 log('no hello: %s\n' % line)
51 log('no hello: %s\n' % line)
44 return
52 return
45
53
46 conn.send(b'250 Hello\r\n')
54 conn.send(b'250 Hello\r\n')
47
55
48 line = conn.recv(1024)
56 line = conn.recv(1024)
49 if not line.lower().startswith(b'mail from:'):
57 if not line.lower().startswith(b'mail from:'):
50 log('no mail from: %s\n' % line)
58 log('no mail from: %s\n' % line)
51 return
59 return
52 mailfrom = line[10:].decode().rstrip()
60 mailfrom = line[10:].decode().rstrip()
53 if mailfrom.startswith('<') and mailfrom.endswith('>'):
61 if mailfrom.startswith('<') and mailfrom.endswith('>'):
54 mailfrom = mailfrom[1:-1]
62 mailfrom = mailfrom[1:-1]
55
63
56 conn.send(b'250 Ok\r\n')
64 conn.send(b'250 Ok\r\n')
57
65
58 rcpttos = []
66 rcpttos = []
59 while True:
67 while True:
60 line = conn.recv(1024)
68 line = conn.recv(1024)
61 if not line.lower().startswith(b'rcpt to:'):
69 if not line.lower().startswith(b'rcpt to:'):
62 break
70 break
63 rcptto = line[8:].decode().rstrip()
71 rcptto = line[8:].decode().rstrip()
64 if rcptto.startswith('<') and rcptto.endswith('>'):
72 if rcptto.startswith('<') and rcptto.endswith('>'):
65 rcptto = rcptto[1:-1]
73 rcptto = rcptto[1:-1]
66 rcpttos.append(rcptto)
74 rcpttos.append(rcptto)
67
75
68 conn.send(b'250 Ok\r\n')
76 conn.send(b'250 Ok\r\n')
69
77
70 if not line.lower().strip() == b'data':
78 if not line.lower().strip() == b'data':
71 log('no rcpt to or data: %s' % line)
79 log('no rcpt to or data: %s' % line)
72
80
73 conn.send(b'354 Go ahead\r\n')
81 conn.send(b'354 Go ahead\r\n')
74
82
75 data = b''
83 data = b''
76 while True:
84 while True:
77 line = conn.recv(1024)
85 line = conn.recv(1024)
78 if not line:
86 if not line:
79 log('connection closed before end of data')
87 log('connection closed before end of data')
80 break
88 break
81 data += line
89 data += line
82 if data.endswith(b'\r\n.\r\n'):
90 if data.endswith(b'\r\n.\r\n'):
83 data = data[:-5]
91 data = data[:-5]
84 break
92 break
85
93
86 conn.send(b'250 Ok\r\n')
94 conn.send(b'250 Ok\r\n')
87
95
88 log(
96 log(
89 '%s from=%s to=%s\n%s\n'
97 '%s from=%s to=%s\n%s\n'
90 % (addr[0], mailfrom, ', '.join(rcpttos), data.decode())
98 % (addr[0], mailfrom, ', '.join(rcpttos), data.decode())
91 )
99 )
92
100
93
101
94 def run(host, port, certificate):
102 def run(host, port, certificate):
95 ui = uimod.ui.load()
103 ui = uimod.ui.load()
96 with socket.socket(family, socket.SOCK_STREAM) as s:
104 with socket.socket(family, socket.SOCK_STREAM) as s:
97 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
105 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
98 s.bind((host, port))
106 s.bind((host, port))
99 # log('listening at %s:%d\n' % (host, port))
107 # log('listening at %s:%d\n' % (host, port))
100 s.listen(1)
108 s.listen(1)
101 try:
109 try:
102 while True:
110 while True:
103 conn, addr = s.accept()
111 conn, addr = s.accept()
104 if certificate:
112 if certificate:
105 try:
113 try:
106 conn = sslutil.wrapserversocket(
114 conn = sslutil.wrapserversocket(
107 conn, ui, certfile=certificate
115 conn, ui, certfile=certificate
108 )
116 )
109 except ssl.SSLError as e:
117 except ssl.SSLError as e:
110 log('%s ssl error: %s\n' % (addr[0], e))
118 log('%s ssl error: %s\n' % (addr[0], e))
111 conn.close()
119 conn.close()
112 continue
120 continue
113 log("connection from %s:%s\n" % addr)
121 log("connection from %s:%s\n" % addr)
114 mocksmtpserversession(conn, addr)
122 mocksmtpserversession(conn, addr)
115 conn.close()
123 conn.close()
116 except KeyboardInterrupt:
124 except KeyboardInterrupt:
117 pass
125 pass
118
126
119
127
120 def _encodestrsonly(v):
128 def _encodestrsonly(v):
121 if isinstance(v, type(u'')):
129 if isinstance(v, type(u'')):
122 return v.encode('ascii')
130 return v.encode('ascii')
123 return v
131 return v
124
132
125
133
126 def bytesvars(obj):
134 def bytesvars(obj):
127 unidict = vars(obj)
135 unidict = vars(obj)
128 bd = {k.encode('ascii'): _encodestrsonly(v) for k, v in unidict.items()}
136 bd = {k.encode('ascii'): _encodestrsonly(v) for k, v in unidict.items()}
129 if bd[b'daemon_postexec'] is not None:
137 if bd[b'daemon_postexec'] is not None:
130 bd[b'daemon_postexec'] = [
138 bd[b'daemon_postexec'] = [
131 _encodestrsonly(v) for v in bd[b'daemon_postexec']
139 _encodestrsonly(v) for v in bd[b'daemon_postexec']
132 ]
140 ]
133 return bd
141 return bd
134
142
135
143
136 def main():
144 def main():
137 op = optparse.OptionParser()
145 op = optparse.OptionParser()
138 op.add_option('-d', '--daemon', action='store_true')
146 op.add_option('-d', '--daemon', action='store_true')
139 op.add_option('--daemon-postexec', action='append')
147 op.add_option('--daemon-postexec', action='append')
140 op.add_option('-p', '--port', type=int, default=8025)
148 op.add_option('-p', '--port', type=int, default=8025)
141 op.add_option('-a', '--address', default='localhost')
149 op.add_option('-a', '--address', default='localhost')
142 op.add_option('--pid-file', metavar='FILE')
150 op.add_option('--pid-file', metavar='FILE')
143 op.add_option('--tls', choices=['none', 'smtps'], default='none')
151 op.add_option('--tls', choices=['none', 'smtps'], default='none')
144 op.add_option('--certificate', metavar='FILE')
152 op.add_option('--certificate', metavar='FILE')
145 op.add_option('--logfile', metavar='FILE')
153 op.add_option('--logfile', metavar='FILE')
146
154
147 opts, args = op.parse_args()
155 opts, args = op.parse_args()
148 if (opts.tls == 'smtps') != bool(opts.certificate):
156 if (opts.tls == 'smtps') != bool(opts.certificate):
149 op.error('--certificate must be specified with --tls=smtps')
157 op.error('--certificate must be specified with --tls=smtps')
150
158
151 server.runservice(
159 server.runservice(
152 bytesvars(opts),
160 bytesvars(opts),
153 runfn=lambda: run(opts.address, opts.port, opts.certificate),
161 runfn=lambda: run(opts.address, opts.port, opts.certificate),
154 runargs=[pycompat.sysexecutable, pycompat.fsencode(__file__)]
162 runargs=[pycompat.sysexecutable, pycompat.fsencode(__file__)]
155 + pycompat.sysargv[1:],
163 + pycompat.sysargv[1:],
156 logfile=opts.logfile,
164 logfile=opts.logfile,
157 )
165 )
158
166
159
167
160 if __name__ == '__main__':
168 if __name__ == '__main__':
161 main()
169 main()
General Comments 0
You need to be logged in to leave comments. Login now