1"""Tests for various MIME issues, including the safe_multipart Tool."""
2
3import cherrypy
4from cherrypy._cpcompat import ntou
5from cherrypy.test import helper
6
7
8def setup_server():
9
10    class Root:
11
12        @cherrypy.expose
13        def multipart(self, parts):
14            return repr(parts)
15
16        @cherrypy.expose
17        def multipart_form_data(self, **kwargs):
18            return repr(list(sorted(kwargs.items())))
19
20        @cherrypy.expose
21        def flashupload(self, Filedata, Upload, Filename):
22            return ('Upload: %s, Filename: %s, Filedata: %r' %
23                    (Upload, Filename, Filedata.file.read()))
24
25    cherrypy.config.update({'server.max_request_body_size': 0})
26    cherrypy.tree.mount(Root())
27
28
29#                             Client-side code                             #
30
31
32class MultipartTest(helper.CPWebCase):
33    setup_server = staticmethod(setup_server)
34
35    def test_multipart(self):
36        text_part = ntou('This is the text version')
37        html_part = ntou(
38            """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
39<html>
40<head>
41 <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
42</head>
43<body bgcolor="#ffffff" text="#000000">
44
45This is the <strong>HTML</strong> version
46</body>
47</html>
48""")
49        body = '\r\n'.join([
50            '--123456789',
51            "Content-Type: text/plain; charset='ISO-8859-1'",
52            'Content-Transfer-Encoding: 7bit',
53            '',
54            text_part,
55            '--123456789',
56            "Content-Type: text/html; charset='ISO-8859-1'",
57            '',
58            html_part,
59            '--123456789--'])
60        headers = [
61            ('Content-Type', 'multipart/mixed; boundary=123456789'),
62            ('Content-Length', str(len(body))),
63        ]
64        self.getPage('/multipart', headers, 'POST', body)
65        self.assertBody(repr([text_part, html_part]))
66
67    def test_multipart_form_data(self):
68        body = '\r\n'.join([
69            '--X',
70            'Content-Disposition: form-data; name="foo"',
71            '',
72            'bar',
73            '--X',
74            # Test a param with more than one value.
75            # See
76            # https://github.com/cherrypy/cherrypy/issues/1028
77            'Content-Disposition: form-data; name="baz"',
78            '',
79            '111',
80            '--X',
81            'Content-Disposition: form-data; name="baz"',
82            '',
83            '333',
84            '--X--'
85        ])
86        self.getPage('/multipart_form_data', method='POST',
87                     headers=[(
88                         'Content-Type', 'multipart/form-data;boundary=X'),
89                         ('Content-Length', str(len(body))),
90                     ],
91                     body=body),
92        self.assertBody(
93            repr([('baz', [ntou('111'), ntou('333')]), ('foo', ntou('bar'))]))
94
95
96class SafeMultipartHandlingTest(helper.CPWebCase):
97    setup_server = staticmethod(setup_server)
98
99    def test_Flash_Upload(self):
100        headers = [
101            ('Accept', 'text/*'),
102            ('Content-Type', 'multipart/form-data; '
103             'boundary=----------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6'),
104            ('User-Agent', 'Shockwave Flash'),
105            ('Host', 'www.example.com:54583'),
106            ('Content-Length', '499'),
107            ('Connection', 'Keep-Alive'),
108            ('Cache-Control', 'no-cache'),
109        ]
110        filedata = (b'<?xml version="1.0" encoding="UTF-8"?>\r\n'
111                    b'<projectDescription>\r\n'
112                    b'</projectDescription>\r\n')
113        body = (
114            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
115            b'Content-Disposition: form-data; name="Filename"\r\n'
116            b'\r\n'
117            b'.project\r\n'
118            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
119            b'Content-Disposition: form-data; '
120            b'name="Filedata"; filename=".project"\r\n'
121            b'Content-Type: application/octet-stream\r\n'
122            b'\r\n' +
123            filedata +
124            b'\r\n'
125            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6\r\n'
126            b'Content-Disposition: form-data; name="Upload"\r\n'
127            b'\r\n'
128            b'Submit Query\r\n'
129            # Flash apps omit the trailing \r\n on the last line:
130            b'------------KM7Ij5cH2KM7Ef1gL6ae0ae0cH2gL6--'
131        )
132        self.getPage('/flashupload', headers, 'POST', body)
133        self.assertBody('Upload: Submit Query, Filename: .project, '
134                        'Filedata: %r' % filedata)
135