def unavailable(reason="No preview is available for the requested file."): # Cache some metadata about the preview so we can retrieve it later. key = preview_key(uri) cache.set(key, json.dumps({ "error": True, "size": size, "truncated": False, "type": content_type, }), settings.PREVIEW_METADATA_EXPIRY) return render_page("fe/errors/preview-unavailable.html", request, reason=reason, response=HttpResponseServerError())
def preview_metadata(request): if request.method != "GET": return JsonMessageResponseNotAllowed(["GET"]) try: uri = request.GET["uri"] except KeyError: return JsonMessageResponseBadRequest("No URI parameter given") key = preview_key(uri) metadata = cache.get(key) if metadata: return HttpResponse(metadata, content_type="application/json; charset=UTF-8") return JsonMessageResponseNotFound("Metadata not in cache")
def preview(request): # The standard response upon error. def unavailable(reason="No preview is available for the requested file."): # Cache some metadata about the preview so we can retrieve it later. key = preview_key(uri) cache.set(key, json.dumps({ "error": True, "size": size, "truncated": False, "type": content_type, }), settings.PREVIEW_METADATA_EXPIRY) return render_page("fe/errors/preview-unavailable.html", request, reason=reason, response=HttpResponseServerError()) try: uri = request.GET["uri"] except KeyError: # This is the one case where we won't return the generic preview # unavailable response, since the request URI itself is malformed. logger.error("Malformed request: 'uri' not found in GET variables") return render_page("fe/errors/404.html", request, response=HttpResponseNotFound()) logger.debug("Attempting to preview URI '%s'", uri) # Set initial values for the size and content_type variables so we can call # unavailable() immediately. size = 0 content_type = "application/octet-stream" # Get the actual file size. resp = ls(request) if resp.status_code != 200: logger.warning("Attempted to preview inaccessible URI '%s'", uri) return unavailable("No preview is available as the requested file is inaccessible.") result = json.loads(resp.content) if len(result) != 1 or len(result.values()[0]["files"]) != 1: logger.warning("Unexpected number of ls results for URI '%s'", uri) return unavailable() size = result.values()[0]["files"][0][1] resp = get(request) if resp.status_code != 200: logger.warning("Attempted to preview inaccessible URI '%s'", uri) return unavailable("No preview is available as the requested file is inaccessible.") # Check the content type. if ";" in resp["content-type"]: content_type, charset = resp["content-type"].split(";", 1) else: content_type = resp["content-type"] charset = None if content_type not in settings.PREVIEW_SETTINGS: logger.debug("Preview of URI '%s' unsuccessful due to unknown MIME type '%s'", uri, content_type) return unavailable("Files of this type cannot be previewed.") type_settings = settings.PREVIEW_SETTINGS[content_type] content_type = type_settings.get("override_mime_type", content_type) # Now work with the resulting file. # If the file is beyond the size limit, we'll need to check whether to # truncate it or not. truncated = False contents = [] size = 0 try: for chunk in resp.streaming_content: contents.append(chunk) size += len(chunk) if size > settings.PREVIEW_SIZE_LIMIT: if type_settings.get("truncate", False): logger.debug("URI '%s' is too large: size %d > limit %d; truncating", uri, size, settings.PREVIEW_SIZE_LIMIT) contents[-1] = contents[-1][:settings.PREVIEW_SIZE_LIMIT - size] truncated = True break else: logger.debug("URI '%s' is too large: size %d > limit %d", uri, size, settings.PREVIEW_SIZE_LIMIT) return unavailable("This file is too large to be previewed.") content = "".join(contents) except Exception: logger.exception("Problem when streaming response") return unavailable("The file could not be accessed.") # Cache some metadata about the preview so we can retrieve it later. key = preview_key(uri) cache.set(key, json.dumps({ "error": False, "size": size, "truncated": settings.PREVIEW_SIZE_LIMIT if truncated else False, "type": content_type, }), settings.PREVIEW_METADATA_EXPIRY) # Set up our response. if charset: content_type += ";" + charset response = HttpResponse(content_type=content_type) # This disables content type sniffing in IE, which could otherwise be used # to enable XSS attacks. response["X-Content-Type-Options"] = "nosniff" logger.debug("Preview of URI '%s' successful; sending response", uri) # The original plan was to verify the MIME type that we get back from # Admin, but honestly, it's pretty useless in many cases. Let's just do the # best we can with the extension. if type_settings.get("sanitise"): try: response.write(html.sanitise(content)) except RuntimeError: return unavailable("This HTML file is too deeply nested to be previewed.") except UnicodeEncodeError: return unavailable("This HTML file includes malformed Unicode, and hence can't be previewed.") else: response.write(content) return response