def _check_ignored(self, header): """Check if the passed header is ignored.""" reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) with self.assertLogs(log.rfc6266, logging.ERROR): cd_inline, cd_filename = http.parse_content_disposition(reply) self.assertEqual(cd_filename, DEFAULT_NAME) self.assertTrue(cd_inline)
def _check_filename(self, header, filename): """Check if the passed header has the given filename.""" reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) cd_inline, cd_filename = http.parse_content_disposition(reply) self.assertIsNotNone(cd_filename) self.assertEqual(cd_filename, filename) self.assertFalse(cd_inline)
def fetch(self, reply, fileobj=None, filename=None, auto_remove=False, suggested_filename=None): """Download a QNetworkReply to disk. Args: reply: The QNetworkReply to download. fileobj: The file object to write the answer to. filename: A path to write the data to. auto_remove: Whether to remove the download even if ui -> remove-finished-downloads is set to false. Return: The created DownloadItem. """ if fileobj is not None and filename is not None: raise TypeError("Only one of fileobj/filename may be given!") if not suggested_filename: if filename is not None: suggested_filename = os.path.basename(filename) elif fileobj is not None and getattr(fileobj, 'name', None): suggested_filename = fileobj.name else: _, suggested_filename = http.parse_content_disposition(reply) log.downloads.debug("fetch: {} -> {}".format(reply.url(), suggested_filename)) download = DownloadItem(reply, self._win_id, self) download.cancelled.connect( functools.partial(self.remove_item, download)) if config.get('ui', 'remove-finished-downloads') or auto_remove: download.finished.connect( functools.partial(self.remove_item, download)) download.data_changed.connect( functools.partial(self.on_data_changed, download)) download.error.connect(self.on_error) download.redirected.connect( functools.partial(self.on_redirect, download)) download.basename = suggested_filename idx = len(self.downloads) + 1 download.index = idx self.beginInsertRows(QModelIndex(), idx, idx) self.downloads.append(download) self.endInsertRows() if filename is not None: download.set_filename(filename) elif fileobj is not None: download.set_fileobj(fileobj) download.autoclose = False else: q = self._prepare_question() q.default = _path_suggestion(suggested_filename) q.answered.connect(download.set_filename) q.cancelled.connect(download.cancel) download.cancelled.connect(q.abort) download.error.connect(q.abort) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.ask(q, blocking=False) return download
def on_unsupported_content(self, reply): """Handle an unsupportedContent signal. Most likely this will mean we need to download the reply, but we correct for some common errors the server do. At some point we might want to implement the MIME Sniffing standard here: http://mimesniff.spec.whatwg.org/ """ inline, _suggested_filename = http.parse_content_disposition(reply) download_manager = objreg.get('download-manager', scope='window', window=self._win_id) if not inline: # Content-Disposition: attachment -> force download download_manager.fetch(reply) return mimetype, _rest = http.parse_content_type(reply) if mimetype == 'image/jpg': # Some servers (e.g. the LinkedIn CDN) send a non-standard # image/jpg (instead of image/jpeg, defined in RFC 1341 section # 7.5). If this is the case, we force displaying with a corrected # mimetype. if reply.isFinished(): self.display_content(reply, 'image/jpeg') else: reply.finished.connect( functools.partial(self.display_content, reply, 'image/jpeg')) else: # Unknown mimetype, so download anyways. download_manager.fetch(reply)
def check_unnamed(self, header): """Check if the passed header results in an unnamed attachment.""" reply = self.stubs.FakeNetworkReply( headers={'Content-Disposition': header}) cd_inline, cd_filename = http.parse_content_disposition(reply) assert cd_filename == DEFAULT_NAME assert not cd_inline
def on_unsupported_content(self, reply): """Handle an unsupportedContent signal. Most likely this will mean we need to download the reply, but we correct for some common errors the server do. At some point we might want to implement the MIME Sniffing standard here: http://mimesniff.spec.whatwg.org/ """ inline, suggested_filename = http.parse_content_disposition(reply) download_manager = objreg.get('download-manager', scope='window', window=self._win_id) if not inline: # Content-Disposition: attachment -> force download download_manager.fetch(reply, suggested_filename=suggested_filename) return mimetype, _rest = http.parse_content_type(reply) if mimetype == 'image/jpg': # Some servers (e.g. the LinkedIn CDN) send a non-standard # image/jpg (instead of image/jpeg, defined in RFC 1341 section # 7.5). If this is the case, we force displaying with a corrected # mimetype. if reply.isFinished(): self.display_content(reply, 'image/jpeg') else: reply.finished.connect(functools.partial( self.display_content, reply, 'image/jpeg')) else: # Unknown mimetype, so download anyways. download_manager.fetch(reply, suggested_filename=suggested_filename)
def fetch(self, reply, fileobj=None, filename=None, auto_remove=False): """Download a QNetworkReply to disk. Args: reply: The QNetworkReply to download. fileobj: The file object to write the answer to. filename: A path to write the data to. auto_remove: Whether to remove the download even if ui -> remove-finished-downloads is set to false. Return: The created DownloadItem. """ if fileobj is not None and filename is not None: raise TypeError("Only one of fileobj/filename may be given!") if filename is not None: suggested_filename = os.path.basename(filename) elif fileobj is not None and getattr(fileobj, 'name', None): suggested_filename = fileobj.name else: _inline, suggested_filename = http.parse_content_disposition(reply) log.downloads.debug("fetch: {} -> {}".format(reply.url(), suggested_filename)) download = DownloadItem(reply, self) download.cancelled.connect( functools.partial(self.remove_item, download)) if config.get('ui', 'remove-finished-downloads') or auto_remove: download.finished.connect( functools.partial(self.remove_item, download)) download.data_changed.connect( functools.partial(self.on_data_changed, download)) download.error.connect(self.on_error) download.redirected.connect( functools.partial(self.on_redirect, download)) download.do_retry.connect(self.fetch) download.basename = suggested_filename idx = len(self.downloads) + 1 self.beginInsertRows(QModelIndex(), idx, idx) self.downloads.append(download) self.endInsertRows() if filename is not None: download.set_filename(filename) elif fileobj is not None: download.set_fileobj(fileobj) download.autoclose = False else: q = self._prepare_question() q.default = suggested_filename q.answered.connect(download.set_filename) q.cancelled.connect(download.cancel) download.cancelled.connect(q.abort) download.error.connect(q.abort) message_bridge = objreg.get('message-bridge', scope='window', window=self._win_id) message_bridge.ask(q, blocking=False) return download
def check_filename(self, header, filename, expected_inline=False): """Check if the passed header has the given filename.""" reply = self.stubs.FakeNetworkReply( headers={'Content-Disposition': header}) cd_inline, cd_filename = http.parse_content_disposition(reply) assert cd_filename is not None assert cd_filename == filename assert cd_inline == expected_inline
def check_ignored(self, header): """Check if the passed header is ignored.""" reply = self.stubs.FakeNetworkReply( headers={'Content-Disposition': header}) with self.caplog.at_level(logging.ERROR, 'rfc6266'): # with self.assertLogs(log.rfc6266, logging.ERROR): cd_inline, cd_filename = http.parse_content_disposition(reply) assert cd_filename == DEFAULT_NAME assert cd_inline
def test_attonly(self, stubs): """'attachment' only. UA should offer to download the resource. """ reply = stubs.FakeNetworkReply( headers={'Content-Disposition': 'attachment'}) cd_inline, cd_filename = http.parse_content_disposition(reply) assert not cd_inline assert cd_filename == DEFAULT_NAME
def fetch(self, reply, *, fileobj=None, filename=None, auto_remove=False, suggested_filename=None, prompt_download_directory=None): """Download a QNetworkReply to disk. Args: reply: The QNetworkReply to download. fileobj: The file object to write the answer to. filename: A path to write the data to. auto_remove: Whether to remove the download even if ui -> remove-finished-downloads is set to -1. Return: The created DownloadItem. """ if fileobj is not None and filename is not None: raise TypeError("Only one of fileobj/filename may be given!") if not suggested_filename: if filename is not None: suggested_filename = os.path.basename(filename) elif fileobj is not None and getattr(fileobj, 'name', None): suggested_filename = fileobj.name else: _, suggested_filename = http.parse_content_disposition(reply) log.downloads.debug("fetch: {} -> {}".format(reply.url(), suggested_filename)) download = DownloadItem(reply, self._win_id, self) download.cancelled.connect( functools.partial(self.remove_item, download)) delay = config.get('ui', 'remove-finished-downloads') if delay > -1: download.finished.connect( functools.partial(self.remove_item_delayed, download, delay)) elif auto_remove: download.finished.connect( functools.partial(self.remove_item, download)) download.data_changed.connect( functools.partial(self.on_data_changed, download)) download.error.connect(self.on_error) download.redirected.connect( functools.partial(self.on_redirect, download)) download.basename = suggested_filename idx = len(self.downloads) + 1 download.index = idx self.beginInsertRows(QModelIndex(), idx, idx) self.downloads.append(download) self.endInsertRows() if not self._update_timer.isActive(): self._update_timer.start() if fileobj is not None: download.set_fileobj(fileobj) download.autoclose = False return download if filename is not None: download.set_filename(filename) return download # Neither filename nor fileobj were given, prepare a question filename, q = ask_for_filename( suggested_filename, self._win_id, parent=self, prompt_download_directory=prompt_download_directory, ) # User doesn't want to be asked, so just use the download_dir if filename is not None: download.set_filename(filename) return download # Ask the user for a filename self._postprocess_question(q) q.answered.connect(download.set_filename) q.cancelled.connect(download.cancel) download.cancelled.connect(q.abort) download.error.connect(q.abort) q.ask() return download
def test_none(self, stubs, url): """Test with no filename at all.""" reply = stubs.FakeNetworkReply(url=QUrl(url)) inline, filename = http.parse_content_disposition(reply) assert inline assert filename == 'qutebrowser-download'
def test_parse_content_disposition(caplog, template, stubs, s): """Test parsing headers based on templates which hypothesis completes.""" header = template.format(s) reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) with caplog.atLevel(logging.ERROR, 'rfc6266'): http.parse_content_disposition(reply)
def test_parse_content_disposition(template, stubs, s): """Test parsing headers based on templates which hypothesis completes.""" header = template.format(s) reply = stubs.FakeNetworkReply(headers={"Content-Disposition": header}) http.parse_content_disposition(reply)
def _check_unnamed(self, header): """Check if the passed header results in an unnamed attachment.""" reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) cd_inline, cd_filename = http.parse_content_disposition(reply) self.assertEqual(cd_filename, DEFAULT_NAME) self.assertFalse(cd_inline)
def test_url(self, stubs, url): """Test with a filename in the URL.""" reply = stubs.FakeNetworkReply(url=QUrl(url)) inline, filename = http.parse_content_disposition(reply) assert inline assert filename == 'path'
def test_parse_content_disposition(caplog, template, stubs, s): """Test parsing headers based on templates which hypothesis completes.""" header = template.format(s) reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header}) with caplog.at_level(logging.ERROR, 'rfc6266'): http.parse_content_disposition(reply)