From https://github.com/deluge-torrent/deluge/commit/5d96cfc72f0bfa36d90afd2725aa2216b8073d66 From: Mamoru TASAKA <mtasaka@fedoraproject.org> Date: Thu, 29 Aug 2024 15:31:25 +0900 Subject: [PATCH] [UI] Replace deprecated cgi module with email As PEP 594 says, cgi module is marked as deprecated in python 3.11, and will be removed in 3.13 (actually removed at least in 3.13 rc1). As suggested on PEP 594, replace cgi.parse_header with email.message.EmailMessage introduced in python 3.6. Updated test modify test_download_with_rename_sanitised - With RFC2045 specification, Content-Disposition filenames parameter containing slash (directory separator) must be quoted, so changing as such. Ref: https://peps.python.org/pep-0594/#deprecated-modules Ref: https://peps.python.org/pep-0594/#cgi Closes: https://github.com/deluge-torrent/deluge/pull/462 --- a/deluge/httpdownloader.py +++ b/deluge/httpdownloader.py @@ -6,7 +6,7 @@ # See LICENSE for more details. # -import cgi +import email.message import logging import os.path import zlib @@ -133,9 +133,10 @@ def request_callback(self, response): content_disp = headers.getRawHeaders(b'content-disposition')[0].decode( 'utf-8' ) - content_disp_params = cgi.parse_header(content_disp)[1] - if 'filename' in content_disp_params: - new_file_name = content_disp_params['filename'] + message = email.message.EmailMessage() + message['content-disposition'] = content_disp + new_file_name = message.get_filename() + if new_file_name: new_file_name = sanitise_filename(new_file_name) new_file_name = os.path.join( os.path.split(self.filename)[0], new_file_name @@ -152,7 +153,10 @@ def request_callback(self, response): self.filename = new_file_name cont_type_header = headers.getRawHeaders(b'content-type')[0].decode() - cont_type, params = cgi.parse_header(cont_type_header) + message = email.message.EmailMessage() + message['content-type'] = cont_type_header + cont_type = message.get_content_type() + params = message['content-type'].params # Only re-ecode text content types. encoding = None if cont_type.startswith('text/'): --- a/deluge/tests/test_httpdownloader.py +++ b/deluge/tests/test_httpdownloader.py @@ -206,10 +206,10 @@ async def test_download_with_rename_exists(self): self.assert_contains(filename, 'This file should be called renamed') async def test_download_with_rename_sanitised(self): - url = self.get_url('rename?filename=/etc/passwd') + url = self.get_url('rename?filename="/etc/passwd"') filename = await download_file(url, fname('original')) assert filename == fname('passwd') - self.assert_contains(filename, 'This file should be called /etc/passwd') + self.assert_contains(filename, 'This file should be called "/etc/passwd"') async def test_download_with_attachment_no_filename(self): url = self.get_url('attachment') --- a/deluge/ui/web/json_api.py +++ b/deluge/ui/web/json_api.py @@ -6,7 +6,7 @@ # See LICENSE for more details. # -import cgi +import email.message import json import logging import os @@ -191,7 +191,9 @@ def _on_json_request(self, request): Handler to take the json data as a string and pass it on to the _handle_request method for further processing. """ - content_type, _ = cgi.parse_header(request.getHeader(b'content-type').decode()) + message = email.message.EmailMessage() + message['content-type'] = request.getHeader(b'content-type').decode() + content_type = message.get_content_type() if content_type != 'application/json': message = 'Invalid JSON request content-type: %s' % content_type raise JSONException(message)