diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2f5f617fe..a2cdbdbbe 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1334,6 +1334,9 @@ class CommandDispatcher: scope='window', window=self._win_id) target = None if dest is not None: + dest = downloads.transform_path(dest) + if dest is None: + raise cmdexc.CommandError("Invalid target filename") target = downloads.FileDownloadTarget(dest) tab = self._current_widget() diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 51ccdbfeb..f9b246d3a 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -19,11 +19,13 @@ """Shared QtWebKit/QtWebEngine code for downloads.""" +import re import sys import html import os.path import collections import functools +import pathlib import tempfile import sip @@ -161,6 +163,25 @@ def get_filename_question(*, suggested_filename, url, parent=None): return q +def transform_path(path): + r"""Do platform-specific transformations, like changing E: to E:\. + + Returns None if the path is invalid on the current platform. + """ + if sys.platform != "win32": + return path + path = utils.expand_windows_drive(path) + # Drive dependent working directories are not supported, e.g. + # E:filename is invalid + if re.match(r'[A-Z]:[^\\]', path, re.IGNORECASE): + return None + # Paths like COM1, ... + # See https://github.com/qutebrowser/qutebrowser/issues/82 + if pathlib.Path(path).is_reserved(): + return None + return path + + class NoFilenameError(Exception): """Raised when we can't find out a filename in DownloadTarget.""" diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 2f833c3a3..b50bcb323 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -19,11 +19,9 @@ """Showing prompts above the statusbar.""" -import re import sys import os.path import html -import pathlib import collections import sip @@ -646,31 +644,13 @@ class FilenamePrompt(_BasePrompt): self._file_model.directoryLoaded.connect( lambda: self._file_model.sort(0)) - def _transform_path(self, path): - r"""Do platform-specific transformations, like changing E: to E:\. - - Returns None if the path is invalid on the current platform. - """ - if sys.platform != "win32": - return path - path = utils.expand_windows_drive(path) - # Drive dependent working directories are not supported, e.g. - # E:filename is invalid - if re.match(r'[A-Z]:[^\\]', path, re.IGNORECASE): - return None - # Paths like COM1, ... - # See https://github.com/qutebrowser/qutebrowser/issues/82 - if pathlib.Path(path).is_reserved(): - return None - return path - def _show_error(self, msg): log.prompt.error(msg) QToolTip.showText(self._lineedit.mapToGlobal(QPoint(0, 0)), msg) def accept(self, value=None): text = value if value is not None else self._lineedit.text() - text = self._transform_path(text) + text = downloads.transform_path(text) if text is None: self._show_error("Invalid filename") return False @@ -725,7 +705,7 @@ class DownloadFilenamePrompt(FilenamePrompt): def accept(self, value=None): text = value if value is not None else self._lineedit.text() - text = self._transform_path(text) + text = downloads.transform_path(text) if text is None: self._show_error("Invalid filename") return False diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index b47edf8d8..38c4cc627 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -140,6 +140,16 @@ Feature: Downloading things from a website. And I run :prompt-cancel Then "Invalid filename" should be logged + @windows + Scenario: Downloading a file to a reserved path with :download + When I run :download data/downloads/download.bin --dest=COM1 + Then the error "Invalid target filename" should be shown + + @windows + Scenario: Download a file to a drive-relative working directory with :download + When I run :download data/downloads/download.bin --dest=C:foobar + Then the error "Invalid target filename" should be shown + ## :download-retry Scenario: Retrying a failed download