summary refs log tree commit diff
path: root/synapse/http/server.py
diff options
context:
space:
mode:
authorKegan Dougal <kegan@matrix.org>2014-08-18 16:18:11 +0100
committerKegan Dougal <kegan@matrix.org>2014-08-18 17:18:54 +0100
commit590ab24c8544553ae5c21b729e35dcda998f1cec (patch)
tree5b3f406cb39fb37a3dde8d51250fc39d0b56ecfa /synapse/http/server.py
parentAuth content uploads. Added a mapping function from request > filename. Added... (diff)
downloadsynapse-590ab24c8544553ae5c21b729e35dcda998f1cec.tar.xz
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.
Diffstat (limited to 'synapse/http/server.py')
-rw-r--r--synapse/http/server.py51
1 files 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: