# Copyright 2014-2016 OpenMarket Ltd # Copyright 2020-2021 The Matrix.org Foundation C.I.C. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import logging import re from typing import TYPE_CHECKING, Optional from synapse.http.server import set_corp_headers, set_cors_headers from synapse.http.servlet import RestServlet, parse_boolean, parse_integer from synapse.http.site import SynapseRequest from synapse.media._base import ( DEFAULT_MAX_TIMEOUT_MS, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS, respond_404, ) from synapse.util.stringutils import parse_and_validate_server_name if TYPE_CHECKING: from synapse.media.media_repository import MediaRepository from synapse.server import HomeServer logger = logging.getLogger(__name__) class DownloadResource(RestServlet): PATTERNS = [ re.compile( "/_matrix/media/(r0|v3|v1)/download/(?P[^/]*)/(?P[^/]*)(/(?P[^/]*))?$" ) ] def __init__(self, hs: "HomeServer", media_repo: "MediaRepository"): super().__init__() self.media_repo = media_repo self._is_mine_server_name = hs.is_mine_server_name async def on_GET( self, request: SynapseRequest, server_name: str, media_id: str, file_name: Optional[str] = None, ) -> None: # Validate the server name, raising if invalid parse_and_validate_server_name(server_name) set_cors_headers(request) set_corp_headers(request) request.setHeader( b"Content-Security-Policy", b"sandbox;" b" default-src 'none';" b" script-src 'none';" b" plugin-types application/pdf;" b" style-src 'unsafe-inline';" b" media-src 'self';" b" object-src 'self';", ) # Limited non-standard form of CSP for IE11 request.setHeader(b"X-Content-Security-Policy", b"sandbox;") request.setHeader(b"Referrer-Policy", b"no-referrer") max_timeout_ms = parse_integer( request, "timeout_ms", default=DEFAULT_MAX_TIMEOUT_MS ) max_timeout_ms = min(max_timeout_ms, MAXIMUM_ALLOWED_MAX_TIMEOUT_MS) if self._is_mine_server_name(server_name): await self.media_repo.get_local_media( request, media_id, file_name, max_timeout_ms ) else: allow_remote = parse_boolean(request, "allow_remote", default=True) if not allow_remote: logger.info( "Rejecting request for remote media %s/%s due to allow_remote", server_name, media_id, ) respond_404(request) return await self.media_repo.get_remote_media( request, server_name, media_id, file_name, max_timeout_ms )