From 590ab24c8544553ae5c21b729e35dcda998f1cec Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 18 Aug 2014 16:18:11 +0100 Subject: [PATCH] hs: Make the uploads directory if it doesn't exist. Namespace uploads by the base64 encoded user id of the uploader. Make a reasonable attempt to retry clashing upload paths. Try to guess a sensible file extension depending on the content type. --- synapse/http/server.py | 51 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/synapse/http/server.py b/synapse/http/server.py index 7d6e225e74..8502416fca 100644 --- a/synapse/http/server.py +++ b/synapse/http/server.py @@ -17,16 +17,19 @@ from syutil.jsonutil import ( encode_canonical_json, encode_pretty_printed_json ) -from synapse.api.errors import cs_exception, CodeMessageException +from synapse.api.errors import cs_exception, SynapseError, CodeMessageException +from synapse.util.stringutils import random_string from twisted.internet import defer, reactor from twisted.web import server, resource from twisted.web.server import NOT_DONE_YET from twisted.web.util import redirectTo +import base64 import collections import json import logging +import os logger = logging.getLogger(__name__) @@ -188,14 +191,52 @@ class FileUploadResource(resource.Resource): file_map_func = self.map_request_to_name self.get_name_for_request = file_map_func + if not os.path.isdir(self.directory): + os.mkdir(self.directory) + logger.info("FileUploadResource : Created %s directory.", + self.directory) + @defer.inlineCallbacks def map_request_to_name(self, request): # auth the user auth_user = yield self.auth.get_user_by_req(request) - logger.info("User %s is uploading a file.", auth_user) - defer.returnValue("boo2.png") - def render(self, request): + # namespace all file uploads on the user + prefix = base64.urlsafe_b64encode( + auth_user.to_string() + ).replace('=', '') + + # use a random string for the main portion + main_part = random_string(24) + + # suffix with a file extension if we can make one. This is nice to + # provide a hint to clients on the file information. + suffix = "" + if request.requestHeaders.hasHeader("Content-Type"): + content_type = request.requestHeaders.getRawHeaders( + "Content-Type")[0] + if (content_type.split("/")[0].lower() in + ["image", "video", "audio"]): + suffix = "." + content_type.split("/")[-1] + + file_path = os.path.join(self.directory, prefix + main_part + suffix) + logger.info("User %s is uploading a file to path %s", + auth_user.to_string(), + file_path) + + # keep trying to make a non-clashing file, with a sensible max attempts + attempts = 0 + while os.path.exists(file_path): + main_part = random_string(24) + file_path = os.path.join(self.directory, + prefix + main_part + suffix) + attempts += 1 + if attempts > 25: # really? Really? + raise SynapseError(500, "Unable to create file.") + + defer.returnValue(file_path) + + def render_POST(self, request): self._async_render(request) return server.NOT_DONE_YET @@ -208,7 +249,7 @@ class FileUploadResource(resource.Resource): f.write(request.content.read()) respond_with_json_bytes(request, 200, - json.dumps({"url": "not_implemented2"}), + json.dumps({"path": fname}), send_cors=True) except CodeMessageException as e: