diff --git a/src/tribler/gui/dialogs/createtorrentdialog.py b/src/tribler/gui/dialogs/createtorrentdialog.py index 67e5a4385dd..329a7b73acd 100644 --- a/src/tribler/gui/dialogs/createtorrentdialog.py +++ b/src/tribler/gui/dialogs/createtorrentdialog.py @@ -1,7 +1,10 @@ import os +import re +import typing from PyQt5 import uic from PyQt5.QtCore import QDir, pyqtSignal +from PyQt5.QtGui import QValidator from PyQt5.QtWidgets import QAction, QFileDialog, QSizePolicy, QTreeWidgetItem from tribler.gui.defs import BUTTON_TYPE_NORMAL @@ -17,6 +20,29 @@ def __init__(self, parent): QTreeWidgetItem.__init__(self, parent) +class TorrentNameValidator(QValidator): + """ + Validator used in Torrent name QLineEdit field to disallow multiline text. + If a new line character is detected, then it is converted to space using fixup(). + + Docs: https://doc.qt.io/qtforpython-5/PySide2/QtGui/QValidator.html + """ + ESCAPE_CHARS_REGEX = r'[\n\r\t]' + + def validate(self, text: str, pos: int) -> typing.Tuple['QValidator.State', str, int]: + if re.search(self.ESCAPE_CHARS_REGEX, text): + return QValidator.Intermediate, text, pos + return QValidator.Acceptable, text, pos + + def fixup(self, text: str) -> str: + return re.sub(self.ESCAPE_CHARS_REGEX, ' ', text) + + +def sanitize_filename(filename: str) -> str: + """Removes some selected escape characters from the filename and returns the cleaned value.""" + return re.sub(r'[\n\r\t]', '', filename) + + class CreateTorrentDialog(DialogContainer): create_torrent_notification = pyqtSignal(dict) add_to_channel_selected = pyqtSignal(str) @@ -27,6 +53,7 @@ def __init__(self, parent): uic.loadUi(get_ui_file_path('createtorrentdialog.ui'), self.dialog_widget) self.dialog_widget.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) + self.dialog_widget.create_torrent_name_field.setValidator(TorrentNameValidator(parent=self)) connect(self.dialog_widget.btn_cancel.clicked, self.close_dialog) connect(self.dialog_widget.create_torrent_choose_files_button.clicked, self.on_choose_files_clicked) connect(self.dialog_widget.create_torrent_choose_dir_button.clicked, self.on_choose_dir_clicked) @@ -109,7 +136,9 @@ def on_create_clicked(self, checked): ) return - self.name = self.dialog_widget.create_torrent_name_field.text() + torrent_name = self.dialog_widget.create_torrent_name_field.text() + self.name = sanitize_filename(torrent_name) + description = self.dialog_widget.create_torrent_description_field.toPlainText() is_seed = self.dialog_widget.seed_after_adding_checkbox.isChecked() diff --git a/src/tribler/gui/dialogs/tests/__init__.py b/src/tribler/gui/dialogs/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/tribler/gui/dialogs/tests/test_createtorrentdialog.py b/src/tribler/gui/dialogs/tests/test_createtorrentdialog.py new file mode 100644 index 00000000000..8e923154256 --- /dev/null +++ b/src/tribler/gui/dialogs/tests/test_createtorrentdialog.py @@ -0,0 +1,31 @@ +from PyQt5.QtGui import QValidator + +from tribler.gui.dialogs.createtorrentdialog import sanitize_filename, TorrentNameValidator + + +def test_torrent_name_validator(): + """ + Tests if the torrent name validator marks the input as valid if there are no multiline characters. + Upon fixup, the invalid characters are accepted correctly. + """ + def assert_text_is_valid(validator: QValidator, original_text: str, expected_to_be_valid: bool): + state, text, pos = validator.validate(original_text, len(original_text)) + assert state == QValidator.Acceptable if expected_to_be_valid else QValidator.Intermediate + assert text == original_text + assert pos == len(original_text) + + validator = TorrentNameValidator(None) + + invalid_name = """line 1 + line2.torrent + """ + assert_text_is_valid(validator, invalid_name, expected_to_be_valid=False) + + fixed_name = validator.fixup(invalid_name) + assert_text_is_valid(validator, fixed_name, expected_to_be_valid=True) + + +def test_sanitize_filename(): + original_filename = "This \nIs \r\nA \tTorrent Name.torrent" + expected_sanitized_filename = "This Is A Torrent Name.torrent" + assert sanitize_filename(original_filename) == expected_sanitized_filename diff --git a/src/tribler/gui/tests/test_utilities.py b/src/tribler/gui/tests/test_utilities.py index 6f64fc00187..aaefc860545 100644 --- a/src/tribler/gui/tests/test_utilities.py +++ b/src/tribler/gui/tests/test_utilities.py @@ -6,10 +6,8 @@ import pytest from tribler.gui.utilities import TranslatedString, compose_magnetlink, create_api_key, dict_item_is_any_of, \ - duration_to_string, \ - format_api_key, \ - quote_plus_unicode, set_api_key, unicode_quoter, get_i18n_file_path, I18N_DIR, LANGUAGES_FILE, \ - get_languages_file_content + duration_to_string, format_api_key, get_i18n_file_path, get_languages_file_content, I18N_DIR, LANGUAGES_FILE, \ + quote_plus_unicode, set_api_key, unicode_quoter def test_quoter_char():